Add runstandardplayerphysics builtin, allowing for csqc-based prediction compatible with quakeworld engines, and just generally handy as a less-dumb more-setup alternative to walkmove.

This commit is contained in:
Shpoike 2023-07-22 17:40:33 +01:00
parent 8fc5860e3c
commit 9b8573aa79
23 changed files with 2593 additions and 120 deletions

View file

@ -180,6 +180,8 @@ ADD_EXECUTABLE(quakespasm
# Quake/net_wipx.c
Quake/pl_linux.c
# Quake/pl_win.c
Quake/pmove.c
Quake/pmovetst.c
Quake/pr_cmds.c
Quake/pr_edict.c
Quake/pr_exec.c

View file

@ -290,6 +290,8 @@ OBJS := strlcat.o \
pr_ext.o \
pr_edict.o \
pr_exec.o \
pmove.o \
pmovetst.o \
sv_main.o \
sv_move.o \
sv_phys.o \

View file

@ -306,6 +306,8 @@ OBJS := strlcat.o \
pr_ext.o \
pr_edict.o \
pr_exec.o \
pmove.o \
pmovetst.o \
sv_main.o \
sv_move.o \
sv_phys.o \

View file

@ -268,6 +268,8 @@ OBJS := strlcat.o \
pr_ext.o \
pr_edict.o \
pr_exec.o \
pmove.o \
pmovetst.o \
sv_main.o \
sv_move.o \
sv_phys.o \

View file

@ -260,6 +260,8 @@ OBJS := strlcat.o \
pr_ext.o \
pr_edict.o \
pr_exec.o \
pmove.o \
pmovetst.o \
sv_main.o \
sv_move.o \
sv_phys.o \

View file

@ -234,6 +234,8 @@ OBJS = strlcat.obj &
pr_ext.obj &
pr_edict.obj &
pr_exec.obj &
pmove.obj &
pmovetxt.obj &
sv_main.obj &
sv_move.obj &
sv_phys.obj &

View file

@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "quakedef.h"
#include "bgmusic.h"
#include "pmove.h"
#include "arch_def.h"
#ifdef PLATFORM_UNIX
@ -1192,66 +1193,6 @@ void CL_AccumulateCmd (void)
cl.pendingcmd.seconds = cl.mtime[0] - cl.pendingcmd.servertime;
}
void CL_CSQC_SetInputs(usercmd_t *cmd, qboolean set)
{
if (set)
{
if (qcvm->extglobals.input_timelength)
*qcvm->extglobals.input_timelength = cmd->seconds;
if (qcvm->extglobals.input_angles)
VectorCopy(cmd->viewangles, qcvm->extglobals.input_angles);
if (qcvm->extglobals.input_movevalues)
{
qcvm->extglobals.input_movevalues[0] = cmd->forwardmove;
qcvm->extglobals.input_movevalues[1] = cmd->sidemove;
qcvm->extglobals.input_movevalues[2] = cmd->upmove;
}
if (qcvm->extglobals.input_buttons)
*qcvm->extglobals.input_buttons = cmd->buttons;
if (qcvm->extglobals.input_impulse)
*qcvm->extglobals.input_impulse = cmd->impulse;
if (qcvm->extglobals.input_weapon)
*qcvm->extglobals.input_weapon = cmd->weapon;
if (qcvm->extglobals.input_cursor_screen)
qcvm->extglobals.input_cursor_screen[0] = cmd->cursor_screen[0], qcvm->extglobals.input_cursor_screen[1] = cmd->cursor_screen[1];
if (qcvm->extglobals.input_cursor_trace_start)
VectorCopy(cmd->cursor_start, qcvm->extglobals.input_cursor_trace_start);
if (qcvm->extglobals.input_cursor_trace_endpos)
VectorCopy(cmd->cursor_impact, qcvm->extglobals.input_cursor_trace_endpos);
if (qcvm->extglobals.input_cursor_entitynumber)
*qcvm->extglobals.input_cursor_entitynumber = cmd->cursor_entitynumber;
}
else
{
if (qcvm->extglobals.input_timelength)
cmd->seconds = *qcvm->extglobals.input_timelength;
if (qcvm->extglobals.input_angles)
VectorCopy(qcvm->extglobals.input_angles, cmd->viewangles);
if (qcvm->extglobals.input_movevalues)
{
cmd->forwardmove = qcvm->extglobals.input_movevalues[0];
cmd->sidemove = qcvm->extglobals.input_movevalues[1];
cmd->upmove = qcvm->extglobals.input_movevalues[2];
}
if (qcvm->extglobals.input_buttons)
cmd->buttons = *qcvm->extglobals.input_buttons;
if (qcvm->extglobals.input_impulse)
cmd->impulse = *qcvm->extglobals.input_impulse;
if (qcvm->extglobals.input_weapon)
cmd->weapon = *qcvm->extglobals.input_weapon;
if (qcvm->extglobals.input_cursor_screen)
cmd->cursor_screen[0] = qcvm->extglobals.input_cursor_screen[0], cmd->cursor_screen[1] = qcvm->extglobals.input_cursor_screen[1];
if (qcvm->extglobals.input_cursor_trace_start)
VectorCopy(qcvm->extglobals.input_cursor_trace_start, cmd->cursor_start);
if (qcvm->extglobals.input_cursor_trace_endpos)
VectorCopy(qcvm->extglobals.input_cursor_trace_endpos, cmd->cursor_impact);
if (qcvm->extglobals.input_cursor_entitynumber)
cmd->cursor_entitynumber = *qcvm->extglobals.input_cursor_entitynumber;
}
}
/*
=================
CL_SendCmd
@ -1280,9 +1221,9 @@ void CL_SendCmd (void)
if (cl.qcvm.extfuncs.CSQC_Input_Frame && !cl.qcvm.nogameaccess)
{
PR_SwitchQCVM(&cl.qcvm);
CL_CSQC_SetInputs(&cmd, true);
PR_GetSetInputs(&cmd, true);
PR_ExecuteProgram(cl.qcvm.extfuncs.CSQC_Input_Frame);
CL_CSQC_SetInputs(&cmd, false);
PR_GetSetInputs(&cmd, false);
PR_SwitchQCVM(NULL);
}
@ -1374,12 +1315,16 @@ static void CL_ServerExtension_FullServerinfo_f(void)
{
const char *newserverinfo = Cmd_Argv(1);
Q_strncpy(cl.serverinfo, newserverinfo, sizeof(cl.serverinfo)); //just replace it
PMCL_ServerinfoUpdated();
}
static void CL_ServerExtension_ServerinfoUpdate_f(void)
{
const char *newserverkey = Cmd_Argv(1);
const char *newservervalue = Cmd_Argv(2);
Info_SetKey(cl.serverinfo, sizeof(cl.serverinfo), newserverkey, newservervalue);
PMCL_ServerinfoUpdated();
}
int Sbar_ColorForMap (int m);

View file

@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "quakedef.h"
#include "bgmusic.h"
#include "pmove.h"
#include <setjmp.h>
/*
@ -707,6 +708,9 @@ void Host_ServerFrame (void)
// set the time and clear the general datagram
SV_ClearDatagram ();
//respond to cvar changes
PMSV_UpdateMovevars ();
// check for new clients
SV_CheckForNewClients ();

1355
Quake/pmove.c Normal file

File diff suppressed because it is too large Load diff

171
Quake/pmove.h Normal file
View file

@ -0,0 +1,171 @@
/*
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.
*/
#include "quakedef.h"
#define BUTTON_ATTACK 1
#define BUTTON_JUMP 2
typedef enum {
PM_NORMAL, // normal ground movement
PM_OLD_SPECTATOR, // fly, no clip to world (QW bug)
PM_SPECTATOR, // fly, no clip to world
PM_DEAD, // no acceleration
PM_FLY, // fly, bump into walls
PM_NONE, // can't move
PM_FREEZE, // can't move or look around (TODO)
PM_WALLWALK, // sticks to walls. on ground while near one
PM_6DOF // spaceship mode
} pmtype_t;
#define PMF_JUMP_HELD 1
#define PMF_LADDER 2 //pmove flags. seperate from flags
#define MAX_PHYSENTS 64//2048
typedef struct
{
vec3_t origin;
vec3_t angles;
qmodel_t *model; // only for bsp models
vec3_t mins, maxs; // only for non-bsp models
int info; // for client or server to identify
unsigned int forcecontentsmask; //set from .skin
} physent_t;
typedef struct
{
// player state
vec3_t origin;
vec3_t safeorigin; //valid when safeorigin_known. needed for extrasr4's ladders otherwise they bug out.
vec3_t angles;
vec3_t velocity;
vec3_t gravitydir;
qboolean jump_held;
float jump_secs; // msec since last jump
float waterjumptime;
int pm_type;
vec3_t player_mins;
vec3_t player_maxs;
// world state
int numphysent;
physent_t physents[MAX_PHYSENTS]; // 0 should be the world
// input
usercmd_t cmd;
qboolean onladder;
qboolean safeorigin_known;
// results
int skipent;
int numtouch;
int touchindex[MAX_PHYSENTS];
vec3_t touchvel[MAX_PHYSENTS];
qboolean onground;
int groundent; // index in physents array, only valid
// when onground is true
int waterlevel;
int watertype;
struct world_s *world;
} playermove_t;
typedef struct {
//standard quakeworld
float gravity;
float stopspeed;
float maxspeed;
float spectatormaxspeed;
float maxairspeed;
float accelerate;
float airaccelerate;
float wateraccelerate;
float friction;
float waterfriction;
float flyfriction;
float entgravity;
//extended stuff, sent via serverinfo
float bunnyspeedcap;
float watersinkspeed;
float ktjump;
float edgefriction; //default 2
float jumpspeed;
int walljump;
qboolean slidefix;
qboolean airstep;
qboolean pground;
qboolean stepdown;
qboolean slidyslopes;
qboolean autobunny;
qboolean bunnyfriction; //force at least one frame of friction when bunnying.
int stepheight;
unsigned protocolflags;
unsigned int flags;
} movevars_t;
#define MOVEFLAG_VALID 0x80000000 //to signal that these are actually known. otherwise reserved.
//#define MOVEFLAG_Q2AIRACCELERATE 0x00000001
#define MOVEFLAG_NOGRAVITYONGROUND 0x00000002 //no slope sliding
//#define MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE 0x00000004 //apply half-gravity both before AND after the move, which better matches the curve
#define MOVEFLAG_QWEDGEBOX 0x00010000 //calculate edgefriction using tracebox and a buggy start pos
#define MOVEFLAG_QWCOMPAT (MOVEFLAG_NOGRAVITYONGROUND|MOVEFLAG_QWEDGEBOX)
#define MASK_PLAYERSOLID CONTENTMASK_ANYSOLID
#define CONTENTBIT_EMPTY CONTENTMASK_FROMQ1(CONTENTS_EMPTY)
#define CONTENTBIT_SOLID CONTENTMASK_FROMQ1(CONTENTS_SOLID)
#define CONTENTBIT_WATER CONTENTMASK_FROMQ1(CONTENTS_WATER)
#define CONTENTBIT_SLIME CONTENTMASK_FROMQ1(CONTENTS_SLIME)
#define CONTENTBIT_LAVA CONTENTMASK_FROMQ1(CONTENTS_LAVA)
#define CONTENTBIT_SKY CONTENTMASK_FROMQ1(CONTENTS_SKY)
#define CONTENTBIT_CLIP CONTENTMASK_FROMQ1(CONTENTS_CLIP)
#define CONTENTBIT_LADDER CONTENTMASK_FROMQ1(CONTENTS_LADDER)
#define CONTENTBITS_FLUID (CONTENTBIT_WATER|CONTENTBIT_SLIME|CONTENTBIT_LAVA)
extern movevars_t movevars;
extern playermove_t pmove;
void PM_PlayerMove (float gamespeed);
void PM_Init (void);
void PM_InitBoxHull (void);
void PM_CategorizePosition (void);
int PM_HullPointContents (hull_t *hull, int num, vec3_t p);
int PM_ExtraBoxContents (vec3_t p); //Peeks for HL-style water.
int PM_PointContents (vec3_t point);
qboolean PM_TestPlayerPosition (vec3_t point);
#ifndef __cplusplus
struct trace_s PM_PlayerTrace (vec3_t start, vec3_t stop, unsigned int solidmask);
#endif
//stuff that should be elsewhere...
int SV_HullPointContents (hull_t *hull, int num, vec3_t p);
void SV_Impact (edict_t *e1, edict_t *e2);
void World_AddEntsToPmove(edict_t *ignore, vec3_t boxminmax[2]);
void PMCL_ServerinfoUpdated(void);
void PMSV_SetMoveStats(edict_t *plent, float *fstat, int *istat); //so client has the right movevars as stats.
void PM_Register(void);
#define VectorClear(v) ((v)[0] = (v)[1] = (v)[2] = 0)
#define VectorSet(r,x,y,z) do{(r)[0] = x; (r)[1] = y;(r)[2] = z;}while(0)
#define Length VectorLength
#define VectorNegate(a,b) ((b)[0]=-(a)[0],(b)[1]=-(a)[1],(b)[2]=-(a)[2])

399
Quake/pmovetst.c Normal file
View file

@ -0,0 +1,399 @@
/*
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.
*/
#include "quakedef.h"
#include "pmove.h"
static qboolean PM_TransformedHullCheck (qmodel_t *model, vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, trace_t *trace, vec3_t origin, vec3_t angles);
static hull_t box_hull;
static mclipnode_t box_clipnodes[6];
static mplane_t box_planes[6];
//axis MUST come from VectorAngles, hence the negation. it being normalized means its also safe to use the transpose(MA-vs-dot) as an inverse to put stuff back into the original coord system. Origin is not handled so subtract/add that before/after.
#define QAxisTransform(a, v, c) \
do{ c[0] = DotProduct(a[0], v);\
c[1] =-DotProduct(a[1], v);\
c[2] = DotProduct(a[2], v);}while(0)
#define QAxisDeTransform(a, v, c) \
do{ VectorScale(a[0], v[0], c);\
VectorMA(c, -v[1], a[1], c);\
VectorMA(c, v[2], a[2], c);}while(0)
/*
===================
PM_InitBoxHull
Set up the planes and clipnodes so that the six floats of a bounding box
can just be stored out and get a proper hull_t structure.
===================
*/
void PM_InitBoxHull (void)
{
int i;
int side;
box_hull.clipnodes = box_clipnodes;
box_hull.planes = box_planes;
box_hull.firstclipnode = 0;
box_hull.lastclipnode = 5;
for (i=0 ; i<6 ; i++)
{
box_clipnodes[i].planenum = i;
side = i&1;
box_clipnodes[i].children[side] = CONTENTS_EMPTY;
if (i != 5)
box_clipnodes[i].children[side^1] = i + 1;
else
box_clipnodes[i].children[side^1] = CONTENTS_SOLID;
box_planes[i].type = i>>1;
box_planes[i].normal[i>>1] = 1;
}
}
/*
===================
PM_HullForBox
To keep everything totally uniform, bounding boxes are turned into small
BSP trees instead of being compared directly.
===================
*/
static hull_t *PM_HullForBox (vec3_t mins, vec3_t maxs)
{
box_planes[0].dist = maxs[0];
box_planes[1].dist = mins[0];
box_planes[2].dist = maxs[1];
box_planes[3].dist = mins[1];
box_planes[4].dist = maxs[2];
box_planes[5].dist = mins[2];
return &box_hull;
}
static unsigned int PM_TransformedModelPointContents (qmodel_t *mod, vec3_t p, vec3_t origin, vec3_t angles)
{
vec3_t p_l, axis[3], p_t;
VectorSubtract (p, origin, p_l);
if (mod->type != mod_brush)
return CONTENTMASK_FROMQ1(CONTENTS_EMPTY);
// rotate start and end into the models frame of reference
if (angles[0] || angles[1] || angles[2])
{
AngleVectors (angles, axis[0], axis[1], axis[2]);
QAxisTransform(axis, p_l, p_t);
return CONTENTMASK_FROMQ1(SV_HullPointContents(&mod->hulls[0], mod->hulls[0].firstclipnode, p_t));
}
return CONTENTMASK_FROMQ1(SV_HullPointContents(&mod->hulls[0], mod->hulls[0].firstclipnode, p_l));
}
/*
==================
PM_PointContents
==================
*/
int PM_PointContents (vec3_t p)
{
int num;
unsigned int pc;
physent_t *pe;
qmodel_t *pm;
//check world.
pm = pmove.physents[0].model;
if (!pm || pm->needload)
return CONTENTBIT_EMPTY;
pc = CONTENTMASK_FROMQ1(SV_PointContents(p));
//we need this for e2m2 - waterjumping on to plats wouldn't work otherwise.
for (num = 1; num < pmove.numphysent; num++)
{
pe = &pmove.physents[num];
if (pe->info == pmove.skipent)
continue;
pm = pe->model;
if (pm)
{
if (p[0] >= pe->origin[0]+pm->mins[0] && p[0] <= pe->origin[0]+pm->maxs[0] &&
p[1] >= pe->origin[1]+pm->mins[1] && p[1] <= pe->origin[1]+pm->maxs[1] &&
p[2] >= pe->origin[2]+pm->mins[2] && p[2] <= pe->origin[2]+pm->maxs[2])
{
if (pe->forcecontentsmask)
{
if (PM_TransformedModelPointContents(pm, p, pe->origin, pe->angles) != CONTENTBIT_EMPTY)
pc |= pe->forcecontentsmask;
}
else
pc |= PM_TransformedModelPointContents(pm, p, pe->origin, pe->angles);
}
}
else if (pe->forcecontentsmask)
{
if (p[0] >= pe->origin[0]+pe->mins[0] && p[0] <= pe->origin[0]+pe->maxs[0] &&
p[1] >= pe->origin[1]+pe->mins[1] && p[1] <= pe->origin[1]+pe->maxs[1] &&
p[2] >= pe->origin[2]+pe->mins[2] && p[2] <= pe->origin[2]+pe->maxs[2])
pc |= pe->forcecontentsmask;
}
}
return pc;
}
int PM_ExtraBoxContents (vec3_t p)
{
int num;
int pc = 0;
physent_t *pe;
qmodel_t *pm;
trace_t tr;
for (num = 1; num < pmove.numphysent; num++)
{
pe = &pmove.physents[num];
pm = pe->model;
if (pm)
{
if (pe->forcecontentsmask)
{
if (!PM_TransformedHullCheck(pm, p, p, pmove.player_mins, pmove.player_maxs, &tr, pe->origin, pe->angles))
continue;
if (tr.startsolid || tr.inwater)
pc |= pe->forcecontentsmask;
}
}
else if (pe->forcecontentsmask)
{
if (p[0]+pmove.player_maxs[0] >= pe->origin[0]+pe->mins[0] && p[0]+pmove.player_mins[0] <= pe->origin[0]+pe->maxs[0] &&
p[1]+pmove.player_maxs[1] >= pe->origin[1]+pe->mins[1] && p[1]+pmove.player_mins[1] <= pe->origin[1]+pe->maxs[1] &&
p[2]+pmove.player_maxs[2] >= pe->origin[2]+pe->mins[2] && p[2]+pmove.player_mins[2] <= pe->origin[2]+pe->maxs[2])
pc |= pe->forcecontentsmask;
}
}
return pc;
}
/*
===============================================================================
LINE TESTING IN HULLS
===============================================================================
*/
/*returns if it actually did a trace*/
static qboolean PM_TransformedHullCheck (qmodel_t *model, vec3_t start, vec3_t end, vec3_t player_mins, vec3_t player_maxs, trace_t *trace, vec3_t origin, vec3_t angles)
{
vec3_t start_l, end_l;
int i;
vec3_t axis[3], start_t, end_t;
// subtract origin offset
VectorSubtract (start, origin, start_l);
VectorSubtract (end, origin, end_l);
// sweep the box through the model
if (model && model->type == mod_brush)
{
hull_t *hull = &model->hulls[(player_maxs[0]-player_mins[0] < 3)?0:1];
if (angles[0] || angles[1] || angles[2])
{
AngleVectors (angles, axis[0], axis[1], axis[2]);
QAxisTransform(axis, start_l, start_t);
QAxisTransform(axis, end_l, end_t);
SV_RecursiveHullCheck(hull, start_t, end_t, trace, MASK_PLAYERSOLID);
VectorCopy(trace->plane.normal, end_t); QAxisDeTransform(axis, end_t, trace->plane.normal);
VectorCopy(trace->endpos, end_t); QAxisDeTransform(axis, end_t, trace->endpos);
}
else
{
for (i = 0; i < 3; i++)
{
if (start_l[i]+player_mins[i] > model->maxs[i] && end_l[i] + player_mins[i] > model->maxs[i])
return false;
if (start_l[i]+player_maxs[i] < model->mins[i] && end_l[i] + player_maxs[i] < model->mins[i])
return false;
}
memset (trace, 0, sizeof(trace_t));
trace->fraction = 1;
trace->allsolid = true;
SV_RecursiveHullCheck(hull, start_l, end_l, trace, MASK_PLAYERSOLID);
}
}
else
{
for (i = 0; i < 3; i++)
{
if (start_l[i]+player_mins[i] > box_planes[0+i*2].dist && end_l[i] + player_mins[i] > box_planes[0+i*2].dist)
return false;
if (start_l[i]+player_maxs[i] < box_planes[1+i*2].dist && end_l[i] + player_maxs[i] < box_planes[1+i*2].dist)
return false;
}
memset (trace, 0, sizeof(trace_t));
trace->fraction = 1;
trace->allsolid = true;
SV_RecursiveHullCheck (&box_hull, start_l, end_l, trace, MASK_PLAYERSOLID);
}
trace->endpos[0] += origin[0];
trace->endpos[1] += origin[1];
trace->endpos[2] += origin[2];
return true;
}
/*
================
PM_TestPlayerPosition
Returns false if the given player position is not valid (in solid)
================
*/
qboolean PM_TestPlayerPosition (vec3_t pos)
{
int i;
physent_t *pe;
vec3_t mins, maxs;
hull_t *hull;
trace_t trace;
for (i=0 ; i< pmove.numphysent ; i++)
{
pe = &pmove.physents[i];
if (pe->info == pmove.skipent)
continue;
if (pe->forcecontentsmask && !(pe->forcecontentsmask & MASK_PLAYERSOLID))
continue;
// get the clipping hull
if (pe->model)
{
if (!PM_TransformedHullCheck (pe->model, pos, pos, pmove.player_mins, pmove.player_maxs, &trace, pe->origin, pe->angles))
continue;
if (trace.allsolid)
return false;
}
else
{
VectorSubtract (pe->mins, pmove.player_maxs, mins);
VectorSubtract (pe->maxs, pmove.player_mins, maxs);
hull = PM_HullForBox (mins, maxs);
VectorSubtract(pos, pe->origin, mins);
if (CONTENTMASK_FROMQ1(SV_HullPointContents(hull, hull->firstclipnode, mins)) & MASK_PLAYERSOLID)
return false;
}
}
pmove.safeorigin_known = true;
VectorCopy (pmove.origin, pmove.safeorigin);
return true;
}
/*
================
PM_PlayerTrace
================
*/
trace_t PM_PlayerTrace (vec3_t start, vec3_t end, unsigned int solidmask)
{
trace_t trace, total;
int i;
physent_t *pe;
// fill in a default trace
memset (&total, 0, sizeof(trace_t));
total.fraction = 1;
total.entnum = -1;
VectorCopy (end, total.endpos);
for (i=0 ; i< pmove.numphysent ; i++)
{
pe = &pmove.physents[i];
if (pe->info == pmove.skipent)
continue;
if (pe->forcecontentsmask && !(pe->forcecontentsmask & solidmask))
continue;
if (!pe->model || pe->model->needload)
{
vec3_t mins, maxs;
VectorSubtract (pe->mins, pmove.player_maxs, mins);
VectorSubtract (pe->maxs, pmove.player_mins, maxs);
PM_HullForBox (mins, maxs);
// trace a line through the apropriate clipping hull
if (!PM_TransformedHullCheck (NULL, start, end, pmove.player_mins, pmove.player_maxs, &trace, pe->origin, pe->angles))
continue;
}
else
{
// trace a line through the apropriate clipping hull
if (!PM_TransformedHullCheck (pe->model, start, end, pmove.player_mins, pmove.player_maxs, &trace, pe->origin, pe->angles))
continue;
}
if (trace.allsolid)
trace.startsolid = true;
// if (trace.startsolid)
// trace.fraction = 0;
// did we clip the move?
if (trace.fraction < total.fraction || (trace.startsolid && !total.startsolid))
{
// fix trace up by the offset
total = trace;
total.entnum = i;
}
}
// //this is needed to avoid *2 friction. some id bug.
if (total.startsolid)
total.fraction = 0;
return total;
}
//for use outside the pmove code. lame, but works.
trace_t PM_TraceLine (vec3_t start, vec3_t end)
{
VectorClear(pmove.player_mins);
VectorClear(pmove.player_maxs);
return PM_PlayerTrace(start, end, MASK_PLAYERSOLID);
}

View file

@ -1712,7 +1712,7 @@ static void PF_sv_makestatic (void)
sv.max_statics = nm;
}
st = &sv.static_entities[sv.num_statics];
SV_BuildEntityState(ent, st);
SV_BuildEntityState(NULL, ent, st);
if (st->alpha == ENTALPHA_ZERO)
; //no point
else
@ -2102,7 +2102,7 @@ static void PF_cl_makestatic (void)
stat = cl.static_entities[i];
cl.num_statics++;
SV_BuildEntityState(ent, &stat->baseline);
SV_BuildEntityState(NULL, ent, &stat->baseline);
// copy it to the current state

View file

@ -1208,6 +1208,7 @@ static void PR_MergeEngineFieldDefs (void)
//{"glowmod", ev_vector}, //fullbright tints
//{"fatness", ev_float}, //bloated rendering...
//{"gravitydir", ev_vector}, //says which direction gravity should act for this ent...
{"pmove_flags", ev_float}, //if runstandardplayerphysics is to work, it needs somewhere to track a couple of flags.
};
int maxofs = qcvm->progs->entityfields;

View file

@ -1811,6 +1811,361 @@ static void PF_TraceToss(void)
pr_global_struct->trace_ent = EDICT_TO_PROG(qcvm->edicts);
}
#include "pmove.h"
void PR_GetSetInputs(usercmd_t *cmd, qboolean set)
{
if (set)
{
if (qcvm->extglobals.input_sequence)
*qcvm->extglobals.input_sequence = cmd->sequence;
if (qcvm->extglobals.input_servertime)
*qcvm->extglobals.input_servertime = cmd->servertime;
if (qcvm->extglobals.input_timelength)
*qcvm->extglobals.input_timelength = cmd->seconds;
if (qcvm->extglobals.input_angles)
VectorCopy(cmd->viewangles, qcvm->extglobals.input_angles);
if (qcvm->extglobals.input_movevalues)
{
qcvm->extglobals.input_movevalues[0] = cmd->forwardmove;
qcvm->extglobals.input_movevalues[1] = cmd->sidemove;
qcvm->extglobals.input_movevalues[2] = cmd->upmove;
}
if (qcvm->extglobals.input_buttons)
*qcvm->extglobals.input_buttons = cmd->buttons;
if (qcvm->extglobals.input_impulse)
*qcvm->extglobals.input_impulse = cmd->impulse;
if (qcvm->extglobals.input_weapon)
*qcvm->extglobals.input_weapon = cmd->weapon;
if (qcvm->extglobals.input_cursor_screen)
qcvm->extglobals.input_cursor_screen[0] = cmd->cursor_screen[0], qcvm->extglobals.input_cursor_screen[1] = cmd->cursor_screen[1];
if (qcvm->extglobals.input_cursor_trace_start)
VectorCopy(cmd->cursor_start, qcvm->extglobals.input_cursor_trace_start);
if (qcvm->extglobals.input_cursor_trace_endpos)
VectorCopy(cmd->cursor_impact, qcvm->extglobals.input_cursor_trace_endpos);
if (qcvm->extglobals.input_cursor_entitynumber)
*qcvm->extglobals.input_cursor_entitynumber = cmd->cursor_entitynumber;
}
else
{
if (qcvm->extglobals.input_sequence)
cmd->sequence = *qcvm->extglobals.input_sequence;
if (qcvm->extglobals.input_servertime)
cmd->servertime = *qcvm->extglobals.input_servertime;
if (qcvm->extglobals.input_timelength)
cmd->seconds = *qcvm->extglobals.input_timelength;
if (qcvm->extglobals.input_angles)
VectorCopy(qcvm->extglobals.input_angles, cmd->viewangles);
if (qcvm->extglobals.input_movevalues)
{
cmd->forwardmove = qcvm->extglobals.input_movevalues[0];
cmd->sidemove = qcvm->extglobals.input_movevalues[1];
cmd->upmove = qcvm->extglobals.input_movevalues[2];
}
if (qcvm->extglobals.input_buttons)
cmd->buttons = *qcvm->extglobals.input_buttons;
if (qcvm->extglobals.input_impulse)
cmd->impulse = *qcvm->extglobals.input_impulse;
if (qcvm->extglobals.input_weapon)
cmd->weapon = *qcvm->extglobals.input_weapon;
if (qcvm->extglobals.input_cursor_screen)
cmd->cursor_screen[0] = qcvm->extglobals.input_cursor_screen[0], cmd->cursor_screen[1] = qcvm->extglobals.input_cursor_screen[1];
if (qcvm->extglobals.input_cursor_trace_start)
VectorCopy(qcvm->extglobals.input_cursor_trace_start, cmd->cursor_start);
if (qcvm->extglobals.input_cursor_trace_endpos)
VectorCopy(qcvm->extglobals.input_cursor_trace_endpos, cmd->cursor_impact);
if (qcvm->extglobals.input_cursor_entitynumber)
cmd->cursor_entitynumber = *qcvm->extglobals.input_cursor_entitynumber;
}
}
static void PF_both_pmove(edict_t *e)
{
static vec3_t extents = {256, 256, 256};
vec3_t bounds[2];
eval_t *pmflags = GetEdictFieldValue(e, qcvm->extfields.pmove_flags);
unsigned int fl = (pmflags && pmflags->_float)?pmflags->_float:0;
memset(&pmove, 0, sizeof(pmove));
VectorCopy(e->v.mins, pmove.player_mins);
VectorCopy(e->v.maxs, pmove.player_maxs);
VectorCopy(e->v.oldorigin, pmove.safeorigin); pmove.safeorigin_known = (qcvm==&sv.qcvm); //where we revert to when stuck. only consider this valid for ssqc.
VectorCopy(e->v.origin, pmove.origin);
VectorCopy(e->v.velocity, pmove.velocity);
// VectorCopy(e->v.angles, pmove.angles);
VectorClear(pmove.gravitydir);
pmove.waterjumptime = (e->v.teleport_time>qcvm->time)?e->v.teleport_time - qcvm->time:0;
pmove.jump_held = !!(fl&PMF_JUMP_HELD);
pmove.onladder = !!(fl&PMF_LADDER);
pmove.jump_secs = 0; //has been 0 since Z_EXT_PM_TYPE instead of imposing a delay on rejumps.
pmove.onground = !!((int)e->v.flags & FL_ONGROUND); //in case we're using pm_pground
switch((int)e->v.movetype)
{
case MOVETYPE_WALK: pmove.pm_type = PM_NORMAL; break;
case MOVETYPE_TOSS: //pmove.pm_type = PM_DEAD; break;
case MOVETYPE_BOUNCE: pmove.pm_type = PM_DEAD; break;
case MOVETYPE_FLY: pmove.pm_type = PM_FLY; break;
case MOVETYPE_NOCLIP: pmove.pm_type = PM_SPECTATOR; break;
case MOVETYPE_NONE:
case MOVETYPE_STEP:
case MOVETYPE_PUSH:
case MOVETYPE_FLYMISSILE:
case MOVETYPE_EXT_BOUNCEMISSILE:
case MOVETYPE_EXT_FOLLOW:
default: pmove.pm_type = PM_NONE; break;
}
VectorSubtract(e->v.absmin, extents, bounds[0]);
VectorAdd(e->v.absmax, extents, bounds[1]);
World_AddEntsToPmove(e, bounds);
PR_GetSetInputs(&pmove.cmd, false); //set pmove.cmd to the input_* globals.
PM_PlayerMove(1);
VectorCopy(pmove.safeorigin, e->v.oldorigin);
VectorCopy(pmove.origin, e->v.origin);
VectorCopy(pmove.velocity, e->v.velocity);
// VectorCopy(pmove.angles, e->v.angles);
e->v.teleport_time = (pmove.waterjumptime>0)?qcvm->time + pmove.waterjumptime:0;
//report jumping+onground properly.
if (pmove.jump_held && movevars.autobunny) //make sure the qc thinks we released the button at some point, triggering a new jump sound.
e->v.flags = (int)e->v.flags | FL_JUMPRELEASED;
if (pmove.onground)
{
e->v.flags = (int)e->v.flags | FL_ONGROUND;
e->v.groundentity = (pmove.physents[pmove.groundent].info<0)?0:EDICT_TO_PROG(EDICT_NUM(pmove.physents[pmove.groundent].info));
}
else
e->v.flags = (int)e->v.flags & ~FL_ONGROUND;
//waterlevel+type
e->v.waterlevel = pmove.waterlevel;
if (pmove.watertype & CONTENTBIT_SOLID)
e->v.watertype = CONTENTS_SOLID;
else if (pmove.watertype & CONTENTBIT_SKY)
e->v.watertype = CONTENTS_SKY;
else if (pmove.watertype & CONTENTBIT_LAVA)
e->v.watertype = CONTENTS_LAVA;
else if (pmove.watertype & CONTENTBIT_SLIME)
e->v.watertype = CONTENTS_SLIME;
else if (pmove.watertype & CONTENTBIT_WATER)
e->v.watertype = CONTENTS_WATER;
else
e->v.watertype = CONTENTS_EMPTY;
if (pmflags)
{
pmflags->_float = ((int)pmflags->_float&~(PMF_JUMP_HELD|PMF_LADDER))
| (pmove.jump_held?PMF_JUMP_HELD:0)
| (pmove.onladder?PMF_LADDER:0);
}
//now do the touch events, so we can press buttons etc.
if (e->v.solid)
{
edict_t *other;
int i, n;
// link into place and touch triggers
SV_LinkEdict (e, true);
// touch other objects
for (i=0 ; i<pmove.numtouch ; i++)
{
n = pmove.physents[pmove.touchindex[i]].info;
if (n < 0)
continue; //don't trigger touches for engine-networked ents, if only because edict_num can't cope.
other = EDICT_NUM(n);
//FIXME: should probably prevent multiple touches of the same ent.
SV_Impact(e, other);
}
}
}
cvar_t pm_bunnyspeedcap = {"pm_bunnyspeedcap", "", CVAR_SERVERINFO}; //reduces strafejumps to maxspeed instead of endlessly accelerating
cvar_t pm_bunnyfriction = {"pm_bunnyfriction", "1", CVAR_SERVERINFO}; //forces nq's frame of friction
cvar_t pm_ktjump = {"pm_ktjump", "", CVAR_SERVERINFO};
cvar_t pm_slidefix = {"pm_slidefix", "1", CVAR_SERVERINFO}; //don't bump when going down slopes.
cvar_t pm_airstep = {"pm_airstep", "", CVAR_SERVERINFO}; //allow stepping up stairs when eg jumping
cvar_t pm_pground = {"pm_pground", "", CVAR_SERVERINFO}; //persistent onground state, supposed to be more precise but has gamecode interaction issues.
cvar_t pm_stepdown = {"pm_stepdown", "", CVAR_SERVERINFO}; //glue the player to the ground when going down steps instead of just up. kinda weird.
cvar_t pm_walljump = {"pm_walljump", "", CVAR_SERVERINFO}; //bouncing off the walls.
cvar_t pm_slidyslopes = {"pm_slidyslopes", "", CVAR_SERVERINFO}; //gradually slide down slopes like vanilla nq
cvar_t pm_autobunny = {"pm_autobunny", "", CVAR_SERVERINFO}; //pogostick mode, for lazy noobs
cvar_t pm_watersinkspeed = {"pm_watersinkspeed", "", CVAR_SERVERINFO}; //speed you sink at when idle in water
cvar_t pm_flyfriction = {"pm_flyfriction", "", CVAR_SERVERINFO}; //friction in fly/noclip modes.
cvar_t pm_edgefriction = {"pm_edgefriction", "2",}; //should alias to edgefriction but be listed in the serverinfo as pm_...
cvar_t pm_stepheight = {"pm_stepheight", ""}; //should alias to edgefriction but be listed in the serverinfo as pm_...
extern cvar_t sv_maxspeed;
cvar_t sv_spectatormaxspeed = {"sv_spectatormaxspeed", "500"};
extern cvar_t sv_accelerate;
cvar_t sv_airaccelerate = {"sv_airaccelerate", "0.7"};
cvar_t sv_wateraccelerate = {"sv_wateraccelerate", "10"};
cvar_t sv_waterfriction = {"sv_waterfriction", "4"};
extern cvar_t sv_waterfriction;
extern cvar_t sv_friction;
extern cvar_t sv_gravity;
extern cvar_t sv_stopspeed;
void PM_Register(void)
{
PM_Init();
Cvar_RegisterVariable(&pm_bunnyspeedcap);
Cvar_RegisterVariable(&pm_bunnyfriction);
Cvar_RegisterVariable(&pm_ktjump);
Cvar_RegisterVariable(&pm_slidefix);
Cvar_RegisterVariable(&pm_airstep);
Cvar_RegisterVariable(&pm_pground);
Cvar_RegisterVariable(&pm_stepdown);
Cvar_RegisterVariable(&pm_walljump);
Cvar_RegisterVariable(&pm_slidyslopes);
Cvar_RegisterVariable(&pm_autobunny);
Cvar_RegisterVariable(&pm_watersinkspeed);
Cvar_RegisterVariable(&pm_flyfriction);
Cvar_RegisterVariable(&pm_edgefriction);
Cvar_RegisterVariable(&pm_stepheight);
Cvar_RegisterVariable(&sv_spectatormaxspeed);
Cvar_RegisterVariable(&sv_airaccelerate);
Cvar_RegisterVariable(&sv_wateraccelerate);
Cvar_RegisterVariable(&sv_waterfriction);
}
static movevars_t svmovevars;
void PMSV_UpdateMovevars(void)
{
memset(&svmovevars, 0, sizeof(svmovevars));
svmovevars.accelerate = sv_accelerate.value;
svmovevars.airaccelerate = sv_airaccelerate.value;
svmovevars.friction = sv_friction.value;
svmovevars.gravity = sv_gravity.value;
svmovevars.stopspeed = sv_stopspeed.value;
svmovevars.wateraccelerate = sv_wateraccelerate.value;
svmovevars.waterfriction = sv_waterfriction.value;
svmovevars.entgravity = 1.0;
svmovevars.maxspeed = sv_maxspeed.value;
svmovevars.spectatormaxspeed= sv_spectatormaxspeed.value;
svmovevars.bunnyspeedcap = pm_bunnyspeedcap.value;
svmovevars.ktjump = pm_ktjump.value;
svmovevars.airstep = (pm_airstep.value != 0);
svmovevars.stepheight = *pm_stepheight.string?pm_stepheight.value:18;
svmovevars.stepdown = (pm_stepdown.value != 0);
svmovevars.walljump = (pm_walljump.value);
svmovevars.slidefix = (pm_slidefix.value != 0);
svmovevars.pground = (pm_pground.value != 0);
svmovevars.slidyslopes = (pm_slidyslopes.value!=0);
svmovevars.autobunny = (pm_autobunny.value!=0);
svmovevars.bunnyfriction = (pm_bunnyfriction.value!=0);
svmovevars.watersinkspeed = *pm_watersinkspeed.string?pm_watersinkspeed.value:60;
svmovevars.flyfriction = *pm_flyfriction.string?pm_flyfriction.value:4;
svmovevars.edgefriction = *pm_edgefriction.string?pm_edgefriction.value:2;
svmovevars.protocolflags = sv.protocolflags;
svmovevars.jumpspeed = 270;
svmovevars.maxairspeed = 30;
svmovevars.flags = MOVEFLAG_VALID|MOVEFLAG_NOGRAVITYONGROUND|(*pm_edgefriction.string?0:MOVEFLAG_QWEDGEBOX);
};
static void PF_sv_pmove(void)
{
edict_t *e = G_EDICT(OFS_PARM0);
movevars = svmovevars;
PF_both_pmove(e);
}
void PMSV_SetMoveStats(edict_t *plent, float *fstat, int *istat)
{
eval_t *entgrav = GetEdictFieldValue(plent, qcvm->extfields.gravity);
fstat[STAT_MOVEVARS_STEPHEIGHT] = svmovevars.stepheight;
istat[STAT_MOVEFLAGS] = svmovevars.flags;
fstat[STAT_MOVEVARS_GRAVITY] = svmovevars.gravity;
fstat[STAT_MOVEVARS_STOPSPEED] = svmovevars.stopspeed;
fstat[STAT_MOVEVARS_MAXSPEED] = svmovevars.maxspeed;
fstat[STAT_MOVEVARS_SPECTATORMAXSPEED] = svmovevars.spectatormaxspeed;
fstat[STAT_MOVEVARS_ACCELERATE] = svmovevars.accelerate;
fstat[STAT_MOVEVARS_AIRACCELERATE] = svmovevars.airaccelerate;
fstat[STAT_MOVEVARS_WATERACCELERATE] = svmovevars.wateraccelerate;
fstat[STAT_MOVEVARS_FRICTION] = svmovevars.friction;
fstat[STAT_MOVEVARS_WATERFRICTION] = svmovevars.waterfriction;
fstat[STAT_MOVEVARS_EDGEFRICTION] = svmovevars.edgefriction;
fstat[STAT_MOVEVARS_ENTGRAVITY] = (entgrav && entgrav->_float)?entgrav->_float:1;
fstat[STAT_MOVEVARS_TIMESCALE] = 1; //gamespeed?
// statsf[STAT_MOVEVARS_TICRATE] = host_maxfps.value;
fstat[STAT_MOVEVARS_JUMPVELOCITY] = svmovevars.jumpspeed;
fstat[STAT_MOVEVARS_MAXAIRSPEED] = svmovevars.maxairspeed;
}
static movevars_t clmovevars;
float PMCL_GetKeyValue(const char *key, float def)
{
char buf[64];
const char *v;
v = Info_GetKey(cl.serverinfo, key, buf, sizeof(buf));
if (*v)
return atof(v);
return def;
}
void PMCL_ServerinfoUpdated(void)
{
memset(&clmovevars, 0, sizeof(clmovevars));
clmovevars.accelerate = PMCL_GetKeyValue("sv_accelerate", 10);
clmovevars.airaccelerate = PMCL_GetKeyValue("sv_airaccelerate", 0.7);
clmovevars.friction = PMCL_GetKeyValue("sv_friction", 4);
clmovevars.gravity = PMCL_GetKeyValue("sv_gravity", 800);
clmovevars.stopspeed = PMCL_GetKeyValue("sv_stopspeed", 100);
clmovevars.wateraccelerate = PMCL_GetKeyValue("sv_wateraccelerate", 10);
clmovevars.waterfriction = PMCL_GetKeyValue("sv_waterfriction", 4);
clmovevars.entgravity = 1.0;
clmovevars.maxspeed = PMCL_GetKeyValue("sv_maxspeed", 320);
clmovevars.spectatormaxspeed = PMCL_GetKeyValue("sv_spectatormaxspeed", 500);
clmovevars.bunnyspeedcap = PMCL_GetKeyValue("pm_bunnyspeedcap", 0);
clmovevars.ktjump = PMCL_GetKeyValue("pm_ktjump", 0);
clmovevars.airstep = PMCL_GetKeyValue("pm_airstep", 0);
clmovevars.stepheight = PMCL_GetKeyValue("pm_stepheight", 18);
clmovevars.stepdown = PMCL_GetKeyValue("pm_stepdown", 0);
clmovevars.walljump = PMCL_GetKeyValue("pm_walljump", 0);
clmovevars.slidefix = PMCL_GetKeyValue("pm_slidefix", 0);
clmovevars.pground = PMCL_GetKeyValue("pm_pground", 0);
clmovevars.slidyslopes = PMCL_GetKeyValue("pm_slidyslopes", 0);
clmovevars.autobunny = PMCL_GetKeyValue("pm_autobunny", 0);
clmovevars.bunnyfriction = PMCL_GetKeyValue("pm_bunnyfriction", 0);
clmovevars.watersinkspeed = PMCL_GetKeyValue("*pm_watersinkspeed", 60);
clmovevars.flyfriction = PMCL_GetKeyValue("*pm_flyfriction", 4);
clmovevars.edgefriction = PMCL_GetKeyValue("*pm_edgefriction", 2);
clmovevars.protocolflags = cl.protocolflags;
clmovevars.flags = MOVEFLAG_VALID|MOVEFLAG_NOGRAVITYONGROUND|((PMCL_GetKeyValue("pm_edgefriction", -1000)!=-1000)?0:MOVEFLAG_QWEDGEBOX);
clmovevars.jumpspeed = 270;
clmovevars.maxairspeed = 30;
};
static void PF_cs_pmove(void)
{
edict_t *e = G_EDICT(OFS_PARM0);
movevars = clmovevars;
if (cl.protocol_pext2 & PEXT2_PREDINFO)
{ //protocol provides movevars as stats...
float *fstat = (cl.protocol == PROTOCOL_VERSION_DP7)?(float*)cl.stats:cl.statsf; //DPP5 is hideous, aim for compat though.
movevars.stepheight = fstat[STAT_MOVEVARS_STEPHEIGHT];
movevars.flags = cl.stats[STAT_MOVEFLAGS];
movevars.gravity = fstat[STAT_MOVEVARS_GRAVITY];
movevars.stopspeed = fstat[STAT_MOVEVARS_STOPSPEED];
movevars.maxspeed = fstat[STAT_MOVEVARS_MAXSPEED];
movevars.spectatormaxspeed = fstat[STAT_MOVEVARS_SPECTATORMAXSPEED];
movevars.accelerate = fstat[STAT_MOVEVARS_ACCELERATE];
movevars.airaccelerate = fstat[STAT_MOVEVARS_AIRACCELERATE];
movevars.wateraccelerate = fstat[STAT_MOVEVARS_WATERACCELERATE];
movevars.friction = fstat[STAT_MOVEVARS_FRICTION];
movevars.waterfriction = fstat[STAT_MOVEVARS_WATERFRICTION];
movevars.edgefriction = fstat[STAT_MOVEVARS_EDGEFRICTION];
movevars.entgravity = fstat[STAT_MOVEVARS_ENTGRAVITY];
movevars.jumpspeed = fstat[STAT_MOVEVARS_JUMPVELOCITY];
movevars.maxairspeed = fstat[STAT_MOVEVARS_MAXAIRSPEED];
}
PF_both_pmove(e);
}
//model stuff
void SetMinMaxSize (edict_t *e, float *minvec, float *maxvec, qboolean rotate);
static void PF_sv_setmodelindex(void)
@ -6942,7 +7297,6 @@ static void PF_cs_setlistener(void)
VectorCopy(up, cl.listener_axis[2]);
}
void CL_CSQC_SetInputs(usercmd_t *cmd, qboolean set);
static void PF_cs_getinputstate(void)
{
unsigned int seq = G_FLOAT(OFS_PARM0);
@ -6950,12 +7304,13 @@ static void PF_cs_getinputstate(void)
{ //the partial/pending frame!
G_FLOAT(OFS_RETURN) = 1;
cl.pendingcmd.seconds = cl.time - cl.pendingcmd.servertime; //make sure this is kept current. its important for smoothness.
CL_CSQC_SetInputs(&cl.pendingcmd, true);
cl.pendingcmd.sequence = seq;
PR_GetSetInputs(&cl.pendingcmd, true);
}
else if (cl.movecmds[seq&MOVECMDS_MASK].sequence == seq)
{ //valid sequence slot.
G_FLOAT(OFS_RETURN) = 1;
CL_CSQC_SetInputs(&cl.movecmds[seq&MOVECMDS_MASK], true);
PR_GetSetInputs(&cl.movecmds[seq&MOVECMDS_MASK], true);
}
else
G_FLOAT(OFS_RETURN) = 0; //invalid
@ -7540,7 +7895,7 @@ static struct
{"getmousepos", PF_NoSSQC, PF_NoCSQC, 344, PF_m_getmousepos,66, D("vector()", "Nasty convoluted DP extension. Typically returns deltas instead of positions. Use CSQC_InputEvent for such things in csqc mods.")}, // #344 This is a DP extension
{"getinputstate", PF_NoSSQC, PF_cs_getinputstate,345, PF_NoMenu, D("float(float inputsequencenum)", "Looks up an input frame from the log, setting the input_* globals accordingly.\nThe sequence number range used for prediction should normally be servercommandframe < sequence <= clientcommandframe.\nThe sequence equal to clientcommandframe will change between input frames.")},// (EXT_CSQC)
{"setsensitivityscaler",PF_NoSSQC, PF_cl_setsensitivity,346, PF_NoMenu, D("void(float sens)", "Temporarily scales the player's mouse sensitivity based upon something like zoom, avoiding potential cvar saving and thus corruption.")},// (EXT_CSQC)
// {"runstandardplayerphysics",NULL, PF_FullCSQCOnly, 347, PF_NoMenu, D("void(entity ent)", "Perform the engine's standard player movement prediction upon the given entity using the input_* globals to describe movement.")},
{"runstandardplayerphysics",PF_sv_pmove,PF_cs_pmove, 347, PF_NoMenu, D("void(entity ent)", "Perform the engine's standard player movement prediction upon the given entity using the input_* globals to describe movement.")},
{"getplayerkeyvalue",NULL, PF_cl_playerkey_s, 348, PF_NoMenu, D("string(float playernum, string keyname)", "Look up a player's userinfo, to discover things like their name, topcolor, bottomcolor, skin, team, *ver.\nAlso includes scoreboard info like frags, ping, pl, userid, entertime, as well as voipspeaking and voiploudness.")},// (EXT_CSQC)
{"getplayerkeyfloat",NULL, PF_cl_playerkey_f, 0, PF_NoMenu, D("float(float playernum, string keyname, optional float assumevalue)", "Cheaper version of getplayerkeyvalue that avoids the need for so many tempstrings.")},
{"isdemo", PF_NoSSQC, PF_cl_isdemo, 349, PF_cl_isdemo,349, D("float()", "Returns if the client is currently playing a demo or not")},// (EXT_CSQC)
@ -8311,6 +8666,17 @@ void PR_EnableExtensions(ddef_t *pr_globaldefs)
#undef QCEXTGLOBAL_INT
#undef QCEXTGLOBAL_VECTOR
//some need guarentees they exist (inputs get copied into globals and then back out)
#define QCEXTGLOBAL_FLOAT(n) if (!qcvm->extglobals.n) qcvm->extglobals.n = &qcvm->fallback_##n;
#define QCEXTGLOBAL_VECTOR(n) if (!qcvm->extglobals.n) qcvm->extglobals.n = &qcvm->fallback_##n[0];
#define QCEXTGLOBAL_INT QCEXTGLOBAL_FLOAT
#define QCEXTGLOBAL_UINT64 QCEXTGLOBAL_FLOAT
QCEXTGLOBALS_INPUTS
#undef QCEXTGLOBAL_FLOAT
#undef QCEXTGLOBAL_VECTOR
#undef QCEXTGLOBAL_INT
#undef QCEXTGLOBAL_UINT64
//any #0 functions are remapped to their builtins here, so we don't have to tweak the VM in an obscure potentially-breaking way.
for (i = 0; i < (unsigned int)qcvm->progs->numfunctions; i++)
{

View file

@ -127,6 +127,7 @@ int NUM_FOR_EDICT(edict_t*);
#define G_FLOAT(o) (qcvm->globals[o])
#define G_INT(o) (*(int *)&qcvm->globals[o])
#define G_UINT(o) (*(unsigned int *)&qcvm->globals[o])
#define G_INT64(o) (*(qcsint64_t *)&qcvm->globals[o])
#define G_UINT64(o) (*(qcuint64_t *)&qcvm->globals[o])
#define G_DOUBLE(o) (*(qcdouble_t *)&qcvm->globals[o])
@ -227,7 +228,9 @@ struct pr_extglobals_s
QCEXTGLOBAL_FLOAT(time)\
QCEXTGLOBAL_FLOAT(frametime)\
//end
#define QCEXTGLOBALS_GAME \
#define QCEXTGLOBALS_INPUTS \
QCEXTGLOBAL_FLOAT(input_sequence)\
QCEXTGLOBAL_FLOAT(input_servertime)\
QCEXTGLOBAL_FLOAT(input_timelength)\
QCEXTGLOBAL_VECTOR(input_movevalues)\
QCEXTGLOBAL_VECTOR(input_angles)\
@ -238,6 +241,9 @@ struct pr_extglobals_s
QCEXTGLOBAL_VECTOR(input_cursor_trace_start)\
QCEXTGLOBAL_VECTOR(input_cursor_trace_endpos)\
QCEXTGLOBAL_FLOAT(input_cursor_entitynumber)\
//end
#define QCEXTGLOBALS_GAME \
QCEXTGLOBALS_INPUTS \
QCEXTGLOBAL_FLOAT(physics_mode)\
//end
#define QCEXTGLOBALS_CSQC \
@ -283,6 +289,7 @@ struct pr_extfields_s
/*stuff used by csqc+ssqc, but not menu*/ \
QCEXTFIELD(customphysics, ".void()")/*function*/ \
QCEXTFIELD(gravity, ".float") /*float*/ \
QCEXTFIELD(pmove_flags, ".float") /*float, mostly to hold jump_held*/ \
//end of list
#define QCEXTFIELDS_CL \
QCEXTFIELD(frame2, ".float") /*for csqc's addentity builtin.*/ \
@ -428,6 +435,17 @@ struct qcvm_s
//originally from world.c
areanode_t areanodes[AREA_NODES];
int numareanodes;
#define QCEXTGLOBAL_FLOAT(n) float fallback_##n;
#define QCEXTGLOBAL_VECTOR(n) vec3_t fallback_##n;
#define QCEXTGLOBAL_INT(n) int fallback_##n;
#define QCEXTGLOBAL_UINT64(n) uint64_t fallback_##n;
QCEXTGLOBALS_INPUTS
#undef QCEXTGLOBAL_FLOAT
#undef QCEXTGLOBAL_VECTOR
#undef QCEXTGLOBAL_INT
#undef QCEXTGLOBAL_UINT64
};
extern globalvars_t *pr_global_struct;
@ -440,6 +458,8 @@ extern qcvm_t *qcvm;
void PR_SwitchQCVM(qcvm_t *nvm);
#endif
void PR_GetSetInputs(usercmd_t *cmd, qboolean set);
extern const builtin_t pr_ssqcbuiltins[];
extern const int pr_ssqcnumbuiltins;
extern const builtin_t pr_csqcbuiltins[];

View file

@ -73,9 +73,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define PEXT2_PREDINFO 0x00000020 //provides input acks and reworks stats such that clc_clientdata becomes redundant.
#define PEXT2_NEWSIZEENCODING 0x00000040 //richer size encoding, for more precise bboxes.
#define PEXT2_INFOBLOBS 0x00000080 //unbounded userinfo
#define PEXT2_ACCEPTED_CLIENT (PEXT2_SUPPORTED_CLIENT|PEXT2_NEWSIZEENCODING|PEXT2_INFOBLOBS) //pext2 flags that we can parse, but don't want to advertise (for demos)
#define PEXT2_SUPPORTED_CLIENT (PEXT2_PRYDONCURSOR|PEXT2_VOICECHAT|PEXT2_SETANGLEDELTA|PEXT2_REPLACEMENTDELTAS|PEXT2_MAXPLAYERS|PEXT2_PREDINFO) //pext2 flags that we understand+support
#define PEXT2_SUPPORTED_SERVER (PEXT2_PRYDONCURSOR|PEXT2_VOICECHAT| PEXT2_REPLACEMENTDELTAS |PEXT2_PREDINFO)
//#define PEXT2_STUNAWARE 0x00000100 //changes the netchan to biased-bigendian (so lead two bits are 1 and not stun's 0, so we don't get confused). not applicable to nq, doesn't change the actual svcs/clcs at all.
//#define PEXT2_VRINPUTS 0x00000200 //clc_move changes, more buttons etc. vr stuff!
//#define PEXT2_LERPTIME 0x00000400 //fitz-bloat parity. redefines UF_16BIT as UF_LERPEND in favour of length coding.
#define PEXT2_ACCEPTED_CLIENT (PEXT2_SUPPORTED_CLIENT|PEXT2_INFOBLOBS) //pext2 flags that we can parse, but don't want to advertise (for demos)
#define PEXT2_SUPPORTED_CLIENT (PEXT2_PRYDONCURSOR|PEXT2_VOICECHAT|PEXT2_SETANGLEDELTA|PEXT2_REPLACEMENTDELTAS|PEXT2_MAXPLAYERS|PEXT2_PREDINFO|PEXT2_NEWSIZEENCODING) //pext2 flags that we understand+support
#define PEXT2_SUPPORTED_SERVER (PEXT2_PRYDONCURSOR|PEXT2_VOICECHAT| PEXT2_REPLACEMENTDELTAS |PEXT2_PREDINFO|PEXT2_NEWSIZEENCODING)
// if the high bit of the servercmd is set, the low bits are fast update flags:
#define U_MOREBITS (1<<0)
@ -489,6 +492,7 @@ typedef struct entity_state_s
#define ES_SOLID_BSP 31
#define ES_SOLID_HULL1 0x80201810
#define ES_SOLID_HULL2 0x80401820
#define ES_SOLID_HAS_EXTRA_BITS(solid) ((solid&0x0707) || (((solid>>16)-32768+32) & 7))
} entity_state_t;
#define EFLAGS_STEP 1
//#define EFLAGS_GLOWTRAIL 2

View file

@ -148,6 +148,44 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define STAT_PUNCHVECTOR_Y 30
#define STAT_PUNCHVECTOR_Z 31
//dp defines these. most are useless but we fill them in for consistency.
//#define STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR 220
//#define STAT_MOVEVARS_AIRCONTROL_PENALTY 221
//#define STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW 222
//#define STAT_MOVEVARS_AIRSTRAFEACCEL_QW 223
//#define STAT_MOVEVARS_AIRCONTROL_POWER 224
#define STAT_MOVEFLAGS 225
//#define STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL 226
//#define STAT_MOVEVARS_WARSOWBUNNY_ACCEL 227
//#define STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED 228
//#define STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL 229
//#define STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO 230
//#define STAT_MOVEVARS_AIRSTOPACCELERATE 231
//#define STAT_MOVEVARS_AIRSTRAFEACCELERATE 232
//#define STAT_MOVEVARS_MAXAIRSTRAFESPEED 233
//#define STAT_MOVEVARS_AIRCONTROL 234
//#define STAT_FRAGLIMIT 235
//#define STAT_TIMELIMIT 236
//#define STAT_MOVEVARS_WALLFRICTION 237
#define STAT_MOVEVARS_FRICTION 238
#define STAT_MOVEVARS_WATERFRICTION 239
//#define STAT_MOVEVARS_TICRATE 240
#define STAT_MOVEVARS_TIMESCALE 241
#define STAT_MOVEVARS_GRAVITY 242
#define STAT_MOVEVARS_STOPSPEED 243
#define STAT_MOVEVARS_MAXSPEED 244
#define STAT_MOVEVARS_SPECTATORMAXSPEED 245
#define STAT_MOVEVARS_ACCELERATE 246
#define STAT_MOVEVARS_AIRACCELERATE 247
#define STAT_MOVEVARS_WATERACCELERATE 248
#define STAT_MOVEVARS_ENTGRAVITY 249
#define STAT_MOVEVARS_JUMPVELOCITY 250
#define STAT_MOVEVARS_EDGEFRICTION 251
#define STAT_MOVEVARS_MAXAIRSPEED 252
#define STAT_MOVEVARS_STEPHEIGHT 253
//#define STAT_MOVEVARS_AIRACCEL_QW 254
//#define STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION 255
// stock defines
//
#define IT_SHOTGUN 1

View file

@ -317,7 +317,7 @@ void SV_DropClient (qboolean crash);
void SVFTE_Ack(client_t *client, int sequence);
void SVFTE_DestroyFrames(client_t *client);
void SV_BuildEntityState(edict_t *ent, entity_state_t *state);
void SV_BuildEntityState(client_t *client, edict_t *ent, entity_state_t *state);
void SV_SendClientMessages (void);
void SV_ClearDatagram (void);

View file

@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// sv_main.c -- server main program
#include "quakedef.h"
#include "pmove.h"
server_t sv;
server_static_t svs;
@ -88,29 +89,12 @@ void SV_CalcStats(client_t *client, int *statsi, float *statsf, const char **sta
statsf[STAT_PUNCHANGLE_Y] = ent->v.punchangle[1];
statsf[STAT_PUNCHANGLE_Z] = ent->v.punchangle[2];
}
/*
if (client->protocol_pext2 & PEXT2_PREDINFO)
{ //prediction needs some info on the server's rules
statsf[STAT_MOVEVARS_FRICTION] = sv_friction.value;
statsf[STAT_MOVEVARS_WATERFRICTION] = sv_waterfriction.value;
statsf[STAT_MOVEVARS_TICRATE] = host_maxfps.value;
statsf[STAT_MOVEVARS_TIMESCALE] = sv_gamespeed.value;
statsf[STAT_MOVEVARS_GRAVITY] = sv_gravity.value;
statsf[STAT_MOVEVARS_STOPSPEED] = sv_stopspeed.value;
statsf[STAT_MOVEVARS_MAXSPEED] = client->maxspeed;
statsf[STAT_MOVEVARS_SPECTATORMAXSPEED] = sv_spectatormaxspeed.value;
statsf[STAT_MOVEVARS_ACCELERATE] = sv_accelerate.value;
statsf[STAT_MOVEVARS_AIRACCELERATE] = sv_airaccelerate.value;
statsf[STAT_MOVEVARS_WATERACCELERATE] = sv_wateraccelerate.value;
statsf[STAT_MOVEVARS_ENTGRAVITY] = client->entgravity/sv_gravity.value;
statsf[STAT_MOVEVARS_JUMPVELOCITY] = sv_jumpvelocity.value; //bah
statsf[STAT_MOVEVARS_EDGEFRICTION] = sv_edgefriction.value;
statsf[STAT_MOVEVARS_MAXAIRSPEED] = client->maxspeed;
statsf[STAT_MOVEVARS_STEPHEIGHT] = 18;
// statsf[STAT_MOVEVARS_AIRACCEL_QW] = 0;
// statsf[STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION] = sv_gravity.value;
PMSV_SetMoveStats(ent, statsf, statsi);
}
*/
for (i = 0; i < sv.numcustomstats; i++)
{
@ -175,8 +159,8 @@ void SV_CalcStats(client_t *client, int *statsi, float *statsf, const char **sta
static unsigned int SVFTE_DeltaPredCalcBits(entity_state_t *from, entity_state_t *to)
{
unsigned int bits = 0;
// if (from && from->pmovetype != to->pmovetype)
// bits |= UFP_MOVETYPE;
if (from && from->pmovetype != to->pmovetype)
bits |= UFP_MOVETYPE;
// if (to->movement[0])
// bits |= UFP_FORWARD;
@ -250,8 +234,8 @@ static unsigned int MSGFTE_DeltaCalcBits(entity_state_t *from, entity_state_t *t
bits |= UF_EFFECTS;
if (to->eflags != from->eflags)
bits |= UF_FLAGS;
// if (to->solidsize != from->solidsize)
// bits |= UF_SOLID;
if (to->solidsize != from->solidsize)
bits |= UF_SOLID;
if (to->scale != from->scale)
bits |= UF_SCALE;
@ -292,6 +276,24 @@ static unsigned int MSGFTE_DeltaCalcBits(entity_state_t *from, entity_state_t *t
return bits;
}
static void MSG_WriteSize16 (sizebuf_t *sb, int sz)
{
if (sz == ES_SOLID_BSP)
MSG_WriteShort(sb, ES_SOLID_BSP);
else if (sz)
{
//decode the 32bit version and recode it.
int x = sz & 255;
int zd = (sz >> 8) & 255;
int zu = ((sz >> 16) & 65535) - 32768;
MSG_WriteShort(sb,
((x>>3)<<0) |
((zd>>3)<<5) |
(((zu+32)>>3)<<10));
}
else
MSG_WriteShort(sb, 0);
}
static void MSGFTE_WriteEntityUpdate(unsigned int bits, entity_state_t *state, sizebuf_t *msg, unsigned int pext2, unsigned int protocolflags)
{
unsigned int predbits = 0;
@ -323,7 +325,7 @@ static void MSGFTE_WriteEntityUpdate(unsigned int bits, entity_state_t *state, s
}
}
// if (!(pext2 & PEXT2_NEWSIZEENCODING)) //was added at the same time
if (!(pext2 & PEXT2_NEWSIZEENCODING)) //was added at the same time
bits &= ~UF_BONEDATA;
/*check if we need more precision for some things*/
@ -472,7 +474,7 @@ static void MSGFTE_WriteEntityUpdate(unsigned int bits, entity_state_t *state, s
if (bits & UF_SOLID)
{
/* if (pext2 & PEXT2_NEWSIZEENCODING)
if (pext2 & PEXT2_NEWSIZEENCODING)
{
if (!state->solidsize)
MSG_WriteByte(msg, 0);
@ -495,7 +497,7 @@ static void MSGFTE_WriteEntityUpdate(unsigned int bits, entity_state_t *state, s
}
else
MSG_WriteSize16(msg, state->solidsize);
*/ }
}
if (bits & UF_FLAGS)
MSG_WriteByte(msg, state->eflags);
@ -1114,7 +1116,7 @@ SV_BuildEntityState
copies edict state into a more compact entity_state_t with all the extension fields etc sorted out and neatened up for network precision.
note: ignores viewmodelforclient and other client-specific stuff.
*/
void SV_BuildEntityState(edict_t *ent, entity_state_t *state)
void SV_BuildEntityState(client_t *client, edict_t *ent, entity_state_t *state)
{
eval_t *val;
state->eflags = 0;
@ -1166,6 +1168,21 @@ void SV_BuildEntityState(edict_t *ent, entity_state_t *state)
state->pmovetype = 0;
state->velocity[0] = state->velocity[1] = state->velocity[2] = 0;
if (client && client->edict && (ent->v.owner == EDICT_TO_PROG(client->edict)))
state->solidsize = 0;
else if (ent->v.solid == SOLID_BSP || (ent->v.skin < 0 && ent->v.modelindex))
state->solidsize = ES_SOLID_BSP;
else if (ent->v.solid == SOLID_BBOX || ent->v.solid == SOLID_SLIDEBOX || ent->v.skin < 0)
{
state->solidsize = CLAMP(0, (int)-ent->v.mins[0], 255);
state->solidsize |= CLAMP(0, (int)-ent->v.mins[2], 255)<<8;
state->solidsize |= CLAMP(0, (int)((ent->v.maxs[2]+32768)), 65535)<<16; /*up can be negative*/
if (state->solidsize == 0x80000000)
state->solidsize = ES_SOLID_NOT; //point sized stuff should just be non-solid. you'll thank me for splitscreens.
}
else
state->solidsize = 0;
}
byte *SV_FatPVS (vec3_t org, qmodel_t *worldmodel);
@ -1288,20 +1305,30 @@ invisible:
maxents += 64;
ents = realloc(ents, maxents*sizeof(*ents));
}
ents[numents].num = e;
SV_BuildEntityState(ent, &ents[numents].state);
SV_BuildEntityState(client, ent, &ents[numents].state);
if ((unsigned int)ents[numents].state.modelindex >= client->limit_models)
ents[numents].state.modelindex = 0;
if (ent == clent) //add velocity, but we only care for the local player (should add prediction for other entities some time too).
{
ents[numents].state.pmovetype = 0;//ent->v.movetype; //fixme: we don't do prediction, so don't tell the client that it can try
if ((int)ent->v.flags & FL_ONGROUND)
eflags |= EFLAGS_ONGROUND;
if (qcvm->extfuncs.SV_RunClientCommand)
ents[numents].state.pmovetype = ent->v.movetype; //looks like prediction is available. assuming SV_RunClientCommand just calls runstandardplayerphysics then we can predict it with matching clientside stuff.
else
ents[numents].state.pmovetype = 0; //fixme: we don't do prediction, so don't tell the client that it can try
if (ents[numents].state.pmovetype)
{ //add some extra pmove flags...
eval_t *pmflags = GetEdictFieldValue(ent, qcvm->extfields.pmove_flags);
if ((int)ent->v.flags & FL_ONGROUND) //nq likes to know this for bob states.
ents[numents].state.pmovetype |= 0x80;
if ((int)pmflags->_float & 1) //'jump_held' so no pogostick surprises.
ents[numents].state.pmovetype |= 0x40;
}
ents[numents].state.velocity[0] = ent->v.velocity[0]*8;
ents[numents].state.velocity[1] = ent->v.velocity[1]*8;
ents[numents].state.velocity[2] = ent->v.velocity[2]*8;
}
/*TODO: other players *should* provide movetype+msec+v_angle+movement+velocity info so they can be extrapolated by fancy clients*/
else if (ent->alpha == ENTALPHA_ZERO && !ent->v.effects) //don't send invisible entities unless they have effects
continue;
val = GetEdictFieldValue(ent, qcvm->extfields.exteriormodeltoclient);
@ -1520,7 +1547,7 @@ void SV_Init (void)
extern cvar_t sv_sound_watersplash; //spike - making these changable is handy...
extern cvar_t sv_sound_land; //spike - and also mutable...
PM_Register();
Cvar_RegisterVariable (&sv_maxvelocity);
Cvar_RegisterVariable (&sv_gravity);
Cvar_RegisterVariable (&sv_friction);

View file

@ -1650,6 +1650,6 @@ void SV_Physics (double frametime)
PR_ExecuteProgram (qcvm->extfuncs.EndFrame);
}
if (!(sv_freezenonclients.value && qcvm == &sv.qcvm))
if (!(sv_freezenonclients.value && qcvm == &sv.qcvm)) //FIXME: this breaks input_timelength
qcvm->time += frametime;
}

View file

@ -392,6 +392,8 @@ void SV_ClientThink (void)
if (sv_player->v.movetype == MOVETYPE_NONE)
return;
if (qcvm->extfuncs.SV_RunClientCommand)
return; //this stuff is handled on inputs. don't corrupt anything.
onground = (int)sv_player->v.flags & FL_ONGROUND;
@ -510,7 +512,7 @@ void SV_ReadClientMove (usercmd_t *move)
return; //okay, we don't care about that then
// calc ping times
host_client->lastmovemessage = sequence;
host_client->lastmovemessage = sequence; //so client can know which input frames still need predicting.
if (!(host_client->protocol_pext2 & PEXT2_PREDINFO))
{
host_client->ping_times[host_client->num_pings%NUM_PING_TIMES]
@ -555,14 +557,8 @@ void SV_ReadClientMove (usercmd_t *move)
//FIXME: attempt to apply physics command now, if the mod has custom physics+csqc-prediction
if (qcvm->extfuncs.SV_RunClientCommand)
if (qcvm->extfuncs.SV_RunClientCommand && host_client->knowntoqc)
{
pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
PR_ExecuteProgram(pr_global_struct->PlayerPreThink);
if (!SV_RunThink (host_client->edict))
return; //ent was removed? o.O
if (timestamp > qcvm->time)
timestamp = qcvm->time; //don't let the client exceed the current time
if (timestamp < qcvm->time-0.5)
@ -573,6 +569,10 @@ void SV_ReadClientMove (usercmd_t *move)
*qcvm->extglobals.input_timelength = timestamp - host_client->lastmovetime;
host_client->lastmovetime = timestamp;
if (qcvm->extglobals.input_sequence)
*qcvm->extglobals.input_sequence = sequence;
if (qcvm->extglobals.input_servertime)
*qcvm->extglobals.input_servertime = timestamp;
if (qcvm->extglobals.input_buttons)
*qcvm->extglobals.input_buttons = buttonbits;
if (qcvm->extglobals.input_impulse)
@ -593,8 +593,13 @@ void SV_ReadClientMove (usercmd_t *move)
*qcvm->extglobals.input_cursor_entitynumber = curs_entity;
pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
PR_ExecuteProgram(qcvm->extfuncs.SV_RunClientCommand);
PR_ExecuteProgram(pr_global_struct->PlayerPreThink);
if (!SV_RunThink (host_client->edict))
return; //ent was removed? o.O
pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
PR_ExecuteProgram(qcvm->extfuncs.SV_RunClientCommand);
pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
PR_ExecuteProgram(pr_global_struct->PlayerPostThink);

View file

@ -262,6 +262,131 @@ void SV_UnlinkEdict (edict_t *ent)
ent->area.prev = ent->area.next = NULL;
}
#include "pmove.h"
static void
World_AreaAddEntsToPmove ( edict_t *ignore, areanode_t *node, vec3_t boxminmax[2] )
{
link_t *l, *next;
edict_t *other;
// touch linked edicts
for (l = node->solid_edicts.next ; l != &node->solid_edicts ; l = next)
{
next = l->next;
other = EDICT_FROM_AREA(l);
if (other == ignore)
continue;
if (other->v.solid != SOLID_BBOX && other->v.solid != SOLID_SLIDEBOX && other->v.solid != SOLID_BSP)
continue;
if (boxminmax[0][0] > other->v.absmax[0]
|| boxminmax[0][1] > other->v.absmax[1]
|| boxminmax[0][2] > other->v.absmax[2]
|| boxminmax[1][0] < other->v.absmin[0]
|| boxminmax[1][1] < other->v.absmin[1]
|| boxminmax[1][2] < other->v.absmin[2] )
continue;
if (PROG_TO_EDICT(other->v.owner) == ignore)
continue; // don't clip against own missiles
if (PROG_TO_EDICT(ignore->v.owner) == other)
continue; // don't clip against owner
if (pmove.numphysent == countof(pmove.physents))
return; //too many... ooer.
pmove.physents[pmove.numphysent].info = NUM_FOR_EDICT(other);
pmove.physents[pmove.numphysent].model = (other->v.solid == SOLID_BSP)?qcvm->GetModel(other->v.modelindex):NULL;
VectorCopy(other->v.origin, pmove.physents[pmove.numphysent].origin);
VectorCopy(other->v.mins, pmove.physents[pmove.numphysent].mins);
VectorCopy(other->v.maxs, pmove.physents[pmove.numphysent].maxs);
VectorCopy(other->v.angles, pmove.physents[pmove.numphysent].angles);
pmove.physents[pmove.numphysent].forcecontentsmask = 0;
if (other->v.skin < 0)
switch((int)other->v.skin)
{
case CONTENTS_WATER: pmove.physents[pmove.numphysent].forcecontentsmask = CONTENTBIT_WATER; break;
case CONTENTS_LAVA: pmove.physents[pmove.numphysent].forcecontentsmask = CONTENTBIT_LAVA; break;
case CONTENTS_SLIME: pmove.physents[pmove.numphysent].forcecontentsmask = CONTENTBIT_SLIME; break;
case CONTENTS_SKY: pmove.physents[pmove.numphysent].forcecontentsmask = CONTENTBIT_SKY; break;
case CONTENTS_CLIP: pmove.physents[pmove.numphysent].forcecontentsmask = CONTENTBIT_CLIP; break;
case CONTENTS_LADDER: pmove.physents[pmove.numphysent].forcecontentsmask = CONTENTBIT_LADDER; break;
}
pmove.numphysent++;
}
// recurse down both sides
if (node->axis == -1)
return;
if ( boxminmax[1][node->axis] > node->dist )
World_AreaAddEntsToPmove ( ignore, node->children[0], boxminmax );
if ( boxminmax[0][node->axis] < node->dist )
World_AreaAddEntsToPmove ( ignore, node->children[1], boxminmax );
}
void World_AddEntsToPmove(edict_t *ignore, vec3_t boxminmax[2])
{
pmove.skipent = NUM_FOR_EDICT(ignore);
pmove.physents[0].model = qcvm->worldmodel;
pmove.numphysent = 1;
World_AreaAddEntsToPmove (ignore, qcvm->areanodes, boxminmax);
//csqc needs to be able to clip against the server's ents, too
if (qcvm == &cl.qcvm)
{
entity_t *touch;
int i;
for (i=1,touch=cl.entities+1 ; i<cl.num_entities ; i++,touch++)
{
if (!touch->model)
continue;
if (touch->netstate.solidsize == ES_SOLID_NOT)
continue; //not relevant
if (pmove.numphysent == countof(pmove.physents))
return; //too many... ooer.
if (touch->netstate.solidsize == ES_SOLID_BSP)
{
if (!touch->model || touch->model->type != mod_brush)
continue;
VectorCopy(touch->model->mins, pmove.physents[pmove.numphysent].mins);
VectorCopy(touch->model->maxs, pmove.physents[pmove.numphysent].maxs);
pmove.physents[pmove.numphysent].model = touch->model;
}
else
{
float *touch_mins = pmove.physents[pmove.numphysent].mins;
float *touch_maxs = pmove.physents[pmove.numphysent].maxs;
touch_maxs[0] = touch_maxs[1] = touch->netstate.solidsize & 255;
touch_mins[0] = touch_mins[1] = -touch_maxs[0];
touch_mins[2] = -(int)((touch->netstate.solidsize >> 8) & 255);
touch_maxs[2] = (int)((touch->netstate.solidsize>>16) & 65535) - 32768;
pmove.physents[pmove.numphysent].model = NULL;
}
pmove.physents[pmove.numphysent].info = -i; //kinda backwards, but oh well. the csqc won't know their numbers.
VectorCopy(touch->origin, pmove.physents[pmove.numphysent].origin);
VectorCopy(touch->angles, pmove.physents[pmove.numphysent].angles);
pmove.physents[pmove.numphysent].forcecontentsmask = 0;
if (touch->skinnum < 0)
switch(touch->skinnum)
{
case CONTENTS_WATER: pmove.physents[pmove.numphysent].forcecontentsmask = CONTENTBIT_WATER; break;
case CONTENTS_LAVA: pmove.physents[pmove.numphysent].forcecontentsmask = CONTENTBIT_LAVA; break;
case CONTENTS_SLIME: pmove.physents[pmove.numphysent].forcecontentsmask = CONTENTBIT_SLIME; break;
case CONTENTS_SKY: pmove.physents[pmove.numphysent].forcecontentsmask = CONTENTBIT_SKY; break;
case CONTENTS_CLIP: pmove.physents[pmove.numphysent].forcecontentsmask = CONTENTBIT_CLIP; break;
case CONTENTS_LADDER: pmove.physents[pmove.numphysent].forcecontentsmask = CONTENTBIT_LADDER; break;
}
pmove.numphysent++;
}
}
}
/*
====================
@ -1208,8 +1333,8 @@ static void World_ClipToNetwork ( moveclip_t *clip )
touch_maxs[0] = touch_maxs[1] = touch->netstate.solidsize & 255;
touch_mins[0] = touch_mins[1] = -touch_maxs[0];
touch_mins[2] = -((touch->netstate.solidsize >>8) & 255);
touch_maxs[2] = ((touch->netstate.solidsize>>16) & 65535) - 32768;
touch_mins[2] = -(int)((touch->netstate.solidsize >>8) & 255);
touch_maxs[2] = (int)((touch->netstate.solidsize>>16) & 65535) - 32768;
VectorSubtract (touch_mins, clip->maxs, hullmins);
VectorSubtract (touch_maxs, clip->mins, hullmaxs);

View file

@ -29,7 +29,7 @@ typedef struct
float dist;
} plane_t;
typedef struct
typedef struct trace_s
{
qboolean allsolid; // if true, plane is not valid
qboolean startsolid; // if true, the initial point was in a solid area
@ -40,6 +40,7 @@ typedef struct
edict_t *ent; // entity the surface is on
int contents; // spike -- the content type(s) that we found.
int entnum; // pmove prefers numbers not pointers.
} trace_t;