quakeforge-old/uquake/sv_user.c
Jeff Teunissen c3f5581b0a Created new subdir: uquake. Pick your favorite U word for the meaning --
Unchained, Ultimate, Ultra, Up Yours, Underworld, Underground, Unified,
Unity, etc. You know the drill.

This takes care of the "standalone" problem with the wrong name, and the
recent snafu with multiple developers working on the same files
simultaneously...expect me (and probably others) to start locking dirs when
updates are taking place.

And yes, this update is really as large as it looks. Software only at the
moment, but I will have the makefile updated to build the GL builds as
well.
1999-12-26 13:51:52 +00:00

629 lines
13 KiB
C

/*
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_user.c -- server code for moving users
#include "quakedef.h"
edict_t *sv_player;
extern cvar_t sv_friction;
cvar_t sv_edgefriction = {"edgefriction", "2"};
extern cvar_t sv_stopspeed;
static vec3_t forward, right, up;
vec3_t wishdir;
float wishspeed;
// world
float *angles;
float *origin;
float *velocity;
qboolean onground;
usercmd_t cmd;
cvar_t sv_idealpitchscale = {"sv_idealpitchscale","0.8"};
/*
===============
SV_SetIdealPitch
===============
*/
#define MAX_FORWARD 6
void SV_SetIdealPitch (void)
{
float angleval, sinval, cosval;
trace_t tr;
vec3_t top, bottom;
float z[MAX_FORWARD];
int i, j;
int step, dir, steps;
if (!((int)sv_player->v.flags & FL_ONGROUND))
return;
angleval = sv_player->v.angles[YAW] * M_PI*2 / 360;
sinval = sin(angleval);
cosval = cos(angleval);
for (i=0 ; i<MAX_FORWARD ; i++)
{
top[0] = sv_player->v.origin[0] + cosval*(i+3)*12;
top[1] = sv_player->v.origin[1] + sinval*(i+3)*12;
top[2] = sv_player->v.origin[2] + sv_player->v.view_ofs[2];
bottom[0] = top[0];
bottom[1] = top[1];
bottom[2] = top[2] - 160;
tr = SV_Move (top, vec3_origin, vec3_origin, bottom, 1, sv_player);
if (tr.allsolid)
return; // looking at a wall, leave ideal the way is was
if (tr.fraction == 1)
return; // near a dropoff
z[i] = top[2] + tr.fraction*(bottom[2]-top[2]);
}
dir = 0;
steps = 0;
for (j=1 ; j<i ; j++)
{
step = z[j] - z[j-1];
if (step > -ON_EPSILON && step < ON_EPSILON)
continue;
if (dir && ( step-dir > ON_EPSILON || step-dir < -ON_EPSILON ) )
return; // mixed changes
steps++;
dir = step;
}
if (!dir)
{
sv_player->v.idealpitch = 0;
return;
}
if (steps < 2)
return;
sv_player->v.idealpitch = -dir * sv_idealpitchscale.value;
}
/*
==================
SV_UserFriction
==================
*/
void SV_UserFriction (void)
{
float *vel;
float speed, newspeed, control;
vec3_t start, stop;
float friction;
trace_t trace;
vel = velocity;
speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]);
if (!speed)
return;
// if the leading edge is over a dropoff, increase friction
start[0] = stop[0] = origin[0] + vel[0]/speed*16;
start[1] = stop[1] = origin[1] + vel[1]/speed*16;
start[2] = origin[2] + sv_player->v.mins[2];
stop[2] = start[2] - 34;
trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, sv_player);
if (trace.fraction == 1.0)
friction = sv_friction.value*sv_edgefriction.value;
else
friction = sv_friction.value;
// apply friction
control = speed < sv_stopspeed.value ? sv_stopspeed.value : speed;
newspeed = speed - host_frametime*control*friction;
if (newspeed < 0)
newspeed = 0;
newspeed /= speed;
vel[0] = vel[0] * newspeed;
vel[1] = vel[1] * newspeed;
vel[2] = vel[2] * newspeed;
}
/*
==============
SV_Accelerate
==============
*/
cvar_t sv_maxspeed = {"sv_maxspeed", "320", false, true};
cvar_t sv_accelerate = {"sv_accelerate", "10"};
#if 0
void SV_Accelerate (vec3_t wishvel)
{
int i;
float addspeed, accelspeed;
vec3_t pushvec;
if (wishspeed == 0)
return;
VectorSubtract (wishvel, velocity, pushvec);
addspeed = VectorNormalize (pushvec);
accelspeed = sv_accelerate.value*host_frametime*addspeed;
if (accelspeed > addspeed)
accelspeed = addspeed;
for (i=0 ; i<3 ; i++)
velocity[i] += accelspeed*pushvec[i];
}
#endif
void SV_Accelerate (void)
{
int i;
float addspeed, accelspeed, currentspeed;
currentspeed = DotProduct (velocity, wishdir);
addspeed = wishspeed - currentspeed;
if (addspeed <= 0)
return;
accelspeed = sv_accelerate.value*host_frametime*wishspeed;
if (accelspeed > addspeed)
accelspeed = addspeed;
for (i=0 ; i<3 ; i++)
velocity[i] += accelspeed*wishdir[i];
}
void SV_AirAccelerate (vec3_t wishveloc)
{
int i;
float addspeed, wishspd, accelspeed, currentspeed;
wishspd = VectorNormalize (wishveloc);
if (wishspd > 30)
wishspd = 30;
currentspeed = DotProduct (velocity, wishveloc);
addspeed = wishspd - currentspeed;
if (addspeed <= 0)
return;
// accelspeed = sv_accelerate.value * host_frametime;
accelspeed = sv_accelerate.value*wishspeed * host_frametime;
if (accelspeed > addspeed)
accelspeed = addspeed;
for (i=0 ; i<3 ; i++)
velocity[i] += accelspeed*wishveloc[i];
}
void DropPunchAngle (void)
{
float len;
len = VectorNormalize (sv_player->v.punchangle);
len -= 10*host_frametime;
if (len < 0)
len = 0;
VectorScale (sv_player->v.punchangle, len, sv_player->v.punchangle);
}
/*
===================
SV_WaterMove
===================
*/
void SV_WaterMove (void)
{
int i;
vec3_t wishvel;
float speed, newspeed, wishspeed, addspeed, accelspeed;
//
// user intentions
//
AngleVectors (sv_player->v.v_angle, forward, right, up);
for (i=0 ; i<3 ; i++)
wishvel[i] = forward[i]*cmd.forwardmove + right[i]*cmd.sidemove;
if (!cmd.forwardmove && !cmd.sidemove && !cmd.upmove)
wishvel[2] -= 60; // drift towards bottom
else
wishvel[2] += cmd.upmove;
wishspeed = Length(wishvel);
if (wishspeed > sv_maxspeed.value)
{
VectorScale (wishvel, sv_maxspeed.value/wishspeed, wishvel);
wishspeed = sv_maxspeed.value;
}
wishspeed *= 0.7;
//
// water friction
//
speed = Length (velocity);
if (speed)
{
newspeed = speed - host_frametime * speed * sv_friction.value;
if (newspeed < 0)
newspeed = 0;
VectorScale (velocity, newspeed/speed, velocity);
}
else
newspeed = 0;
//
// water acceleration
//
if (!wishspeed)
return;
addspeed = wishspeed - newspeed;
if (addspeed <= 0)
return;
VectorNormalize (wishvel);
accelspeed = sv_accelerate.value * wishspeed * host_frametime;
if (accelspeed > addspeed)
accelspeed = addspeed;
for (i=0 ; i<3 ; i++)
velocity[i] += accelspeed * wishvel[i];
}
void SV_WaterJump (void)
{
if (sv.time > sv_player->v.teleport_time
|| !sv_player->v.waterlevel)
{
sv_player->v.flags = (int)sv_player->v.flags & ~FL_WATERJUMP;
sv_player->v.teleport_time = 0;
}
sv_player->v.velocity[0] = sv_player->v.movedir[0];
sv_player->v.velocity[1] = sv_player->v.movedir[1];
}
/*
===================
SV_AirMove
===================
*/
void SV_AirMove (void)
{
int i;
vec3_t wishvel;
float fmove, smove;
AngleVectors (sv_player->v.angles, forward, right, up);
fmove = cmd.forwardmove;
smove = cmd.sidemove;
// hack to not let you back into teleporter
if (sv.time < sv_player->v.teleport_time && fmove < 0)
fmove = 0;
for (i=0 ; i<3 ; i++)
wishvel[i] = forward[i]*fmove + right[i]*smove;
if ( (int)sv_player->v.movetype != MOVETYPE_WALK)
wishvel[2] = cmd.upmove;
else
wishvel[2] = 0;
VectorCopy (wishvel, wishdir);
wishspeed = VectorNormalize(wishdir);
if (wishspeed > sv_maxspeed.value)
{
VectorScale (wishvel, sv_maxspeed.value/wishspeed, wishvel);
wishspeed = sv_maxspeed.value;
}
if ( sv_player->v.movetype == MOVETYPE_NOCLIP)
{ // noclip
VectorCopy (wishvel, velocity);
}
else if ( onground )
{
SV_UserFriction ();
SV_Accelerate ();
}
else
{ // not on ground, so little effect on velocity
SV_AirAccelerate (wishvel);
}
}
/*
===================
SV_ClientThink
the move fields specify an intended velocity in pix/sec
the angle fields specify an exact angular motion in degrees
===================
*/
void SV_ClientThink (void)
{
vec3_t v_angle;
if (sv_player->v.movetype == MOVETYPE_NONE)
return;
onground = (int)sv_player->v.flags & FL_ONGROUND;
origin = sv_player->v.origin;
velocity = sv_player->v.velocity;
DropPunchAngle ();
//
// if dead, behave differently
//
if (sv_player->v.health <= 0)
return;
//
// angles
// show 1/3 the pitch angle and all the roll angle
cmd = host_client->cmd;
angles = sv_player->v.angles;
VectorAdd (sv_player->v.v_angle, sv_player->v.punchangle, v_angle);
angles[ROLL] = V_CalcRoll (sv_player->v.angles, sv_player->v.velocity)*4;
if (!sv_player->v.fixangle)
{
angles[PITCH] = -v_angle[PITCH]/3;
angles[YAW] = v_angle[YAW];
}
if ( (int)sv_player->v.flags & FL_WATERJUMP )
{
SV_WaterJump ();
return;
}
//
// walk
//
if ( (sv_player->v.waterlevel >= 2)
&& (sv_player->v.movetype != MOVETYPE_NOCLIP) )
{
SV_WaterMove ();
return;
}
SV_AirMove ();
}
/*
===================
SV_ReadClientMove
===================
*/
void SV_ReadClientMove (usercmd_t *move)
{
int i;
vec3_t angle;
int bits;
// read ping time
host_client->ping_times[host_client->num_pings%NUM_PING_TIMES]
= sv.time - MSG_ReadFloat ();
host_client->num_pings++;
// read current angles
for (i=0 ; i<3 ; i++)
angle[i] = MSG_ReadAngle ();
VectorCopy (angle, host_client->edict->v.v_angle);
// read movement
move->forwardmove = MSG_ReadShort ();
move->sidemove = MSG_ReadShort ();
move->upmove = MSG_ReadShort ();
// read buttons
bits = MSG_ReadByte ();
host_client->edict->v.button0 = bits & 1;
host_client->edict->v.button2 = (bits & 2)>>1;
i = MSG_ReadByte ();
if (i)
host_client->edict->v.impulse = i;
#ifdef QUAKE2
// read light level
host_client->edict->v.light_level = MSG_ReadByte ();
#endif
}
/*
===================
SV_ReadClientMessage
Returns false if the client should be killed
===================
*/
qboolean SV_ReadClientMessage (void)
{
int ret;
int cmd;
char *s;
do
{
nextmsg:
ret = NET_GetMessage (host_client->netconnection);
if (ret == -1)
{
Sys_Printf ("SV_ReadClientMessage: NET_GetMessage failed\n");
return false;
}
if (!ret)
return true;
MSG_BeginReading ();
while (1)
{
if (!host_client->active)
return false; // a command caused an error
if (msg_badread)
{
Sys_Printf ("SV_ReadClientMessage: badread\n");
return false;
}
cmd = MSG_ReadChar ();
switch (cmd)
{
case -1:
goto nextmsg; // end of message
default:
Sys_Printf ("SV_ReadClientMessage: unknown command char\n");
return false;
case clc_nop:
// Sys_Printf ("clc_nop\n");
break;
case clc_stringcmd:
s = MSG_ReadString ();
if (host_client->privileged)
ret = 2;
else
ret = 0;
if (Q_strncasecmp(s, "status", 6) == 0)
ret = 1;
else if (Q_strncasecmp(s, "god", 3) == 0)
ret = 1;
else if (Q_strncasecmp(s, "notarget", 8) == 0)
ret = 1;
else if (Q_strncasecmp(s, "fly", 3) == 0)
ret = 1;
else if (Q_strncasecmp(s, "name", 4) == 0)
ret = 1;
else if (Q_strncasecmp(s, "noclip", 6) == 0)
ret = 1;
else if (Q_strncasecmp(s, "say", 3) == 0)
ret = 1;
else if (Q_strncasecmp(s, "say_team", 8) == 0)
ret = 1;
else if (Q_strncasecmp(s, "tell", 4) == 0)
ret = 1;
else if (Q_strncasecmp(s, "color", 5) == 0)
ret = 1;
else if (Q_strncasecmp(s, "kill", 4) == 0)
ret = 1;
else if (Q_strncasecmp(s, "pause", 5) == 0)
ret = 1;
else if (Q_strncasecmp(s, "spawn", 5) == 0)
ret = 1;
else if (Q_strncasecmp(s, "begin", 5) == 0)
ret = 1;
else if (Q_strncasecmp(s, "prespawn", 8) == 0)
ret = 1;
else if (Q_strncasecmp(s, "kick", 4) == 0)
ret = 1;
else if (Q_strncasecmp(s, "ping", 4) == 0)
ret = 1;
else if (Q_strncasecmp(s, "give", 4) == 0)
ret = 1;
else if (Q_strncasecmp(s, "ban", 3) == 0)
ret = 1;
if (ret == 2)
Cbuf_InsertText (s);
else if (ret == 1)
Cmd_ExecuteString (s, src_client);
else
Con_DPrintf("%s tried to %s\n", host_client->name, s);
break;
case clc_disconnect:
// Sys_Printf ("SV_ReadClientMessage: client disconnected\n");
return false;
case clc_move:
SV_ReadClientMove (&host_client->cmd);
break;
}
}
} while (ret == 1);
return true;
}
/*
==================
SV_RunClients
==================
*/
void SV_RunClients (void)
{
int i;
for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
{
if (!host_client->active)
continue;
sv_player = host_client->edict;
if (!SV_ReadClientMessage ())
{
SV_DropClient (false); // client misbehaved...
continue;
}
if (!host_client->spawned)
{
// clear client movement until a new packet is received
memset (&host_client->cmd, 0, sizeof(host_client->cmd));
continue;
}
// always pause in single player if in console or menus
if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
SV_ClientThink ();
}
}