X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=sv_phys.c;h=00e046b30bedd61e5069fdc2bc3c6f7409f29903;hp=555aa44b2df3d1c9d1a324f20836a809405658fb;hb=158a7b59e0e074680922cfa35eccddd555aa2e31;hpb=b01d973583201f2b57613d2233572fba9d33f74a diff --git a/sv_phys.c b/sv_phys.c index 555aa44b..00e046b3 100644 --- a/sv_phys.c +++ b/sv_phys.c @@ -20,6 +20,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // sv_phys.c #include "quakedef.h" +// used only for VM_GetTempString +#include "prvm_cmds.h" /* @@ -39,17 +41,24 @@ solid_edge items only clip against bsp models. */ -cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4"}; -cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100"}; -cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800"}; -cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000"}; -cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0"}; -cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18"}; -cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "1"}; -cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1"}; -cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0"}; -cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0"}; -cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1"}; +cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4", "how fast you slow down"}; +cvar_t sv_waterfriction = {CVAR_NOTIFY, "sv_waterfriction","-1", "how fast you slow down, if less than 0 the sv_friction variable is used instead"}; +cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"}; +cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"}; +cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"}; +cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"}; +cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"}; +cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"}; +cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"}; +cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0", "enables simpler/buggier player physics (not recommended)"}; +cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"}; +cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"}; + +cvar_t sv_sound_watersplash = {0, "sv_sound_watersplash", "misc/h2ohit1.wav", "sound to play when MOVETYPE_FLY/TOSS/BOUNCE/STEP entity enters or leaves water (empty cvar disables the sound)"}; +cvar_t sv_sound_land = {0, "sv_sound_land", "demon/dland2.wav", "sound to play when MOVETYPE_STEP entity hits the ground at high speed (empty cvar disables the sound)"}; + +// TODO: move this extern to server.h +extern cvar_t sv_clmovement_waitforinput; #define MOVE_EPSILON 0.01 @@ -64,6 +73,25 @@ void SV_Phys_Init (void) Cvar_RegisterVariable(&sv_freezenonclients); Cvar_RegisterVariable(&sv_playerphysicsqc); + + Cvar_RegisterVariable(&sv_sound_watersplash); + Cvar_RegisterVariable(&sv_sound_land); +} + +/* +============ +SV_TestEntityPosition + +returns true if the entity is in solid currently +============ +*/ +static int SV_TestEntityPosition (prvm_edict_t *ent) +{ + trace_t trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent); + if (trace.startsupercontents & SUPERCONTENTS_SOLID) + return true; + else + return false; } /* @@ -169,25 +197,73 @@ SV_Impact Two entities have touched, so run their touch functions ================== */ -void SV_Impact (prvm_edict_t *e1, prvm_edict_t *e2) +void SV_Impact (prvm_edict_t *e1, trace_t *trace) { int old_self, old_other; + prvm_edict_t *e2 = (prvm_edict_t *)trace->ent; + prvm_eval_t *val; old_self = prog->globals.server->self; old_other = prog->globals.server->other; prog->globals.server->time = sv.time; - if (e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT) + if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT) { prog->globals.server->self = PRVM_EDICT_TO_PROG(e1); prog->globals.server->other = PRVM_EDICT_TO_PROG(e2); + prog->globals.server->trace_allsolid = trace->allsolid; + prog->globals.server->trace_startsolid = trace->startsolid; + prog->globals.server->trace_fraction = trace->fraction; + prog->globals.server->trace_inwater = trace->inwater; + prog->globals.server->trace_inopen = trace->inopen; + VectorCopy (trace->endpos, prog->globals.server->trace_endpos); + VectorCopy (trace->plane.normal, prog->globals.server->trace_plane_normal); + prog->globals.server->trace_plane_dist = trace->plane.dist; + if (trace->ent) + prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace->ent); + else + prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts); + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents))) + val->_float = trace->startsupercontents; + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents))) + val->_float = trace->hitsupercontents; + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags))) + val->_float = trace->hitq3surfaceflags; + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename))) + { + if (trace->hittexture) + { + char *s = VM_GetTempString(); + strlcpy(s, trace->hittexture->name, VM_STRINGTEMP_LENGTH); + val->string = PRVM_SetEngineString(s); + } + else + val->string = 0; + } PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing"); } - if (e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT) + if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT) { prog->globals.server->self = PRVM_EDICT_TO_PROG(e2); prog->globals.server->other = PRVM_EDICT_TO_PROG(e1); + prog->globals.server->trace_allsolid = false; + prog->globals.server->trace_startsolid = false; + prog->globals.server->trace_fraction = 1; + prog->globals.server->trace_inwater = false; + prog->globals.server->trace_inopen = true; + VectorCopy (e2->fields.server->origin, prog->globals.server->trace_endpos); + VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1); + prog->globals.server->trace_plane_dist = 0; + prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1); + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents))) + val->_float = 0; + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents))) + val->_float = 0; + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags))) + val->_float = 0; + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename))) + val->string = 0; PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing"); } @@ -298,15 +374,15 @@ int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal) Con_Print("\n"); #endif - /* - if (trace.startsolid) + if (trace.bmodelstartsolid) { - // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world) + // LordHavoc: note: this code is what makes entities stick in place + // if embedded in world only (you can walk through other objects if + // stuck) // entity is trapped in another solid VectorClear(ent->fields.server->velocity); return 3; } - */ // break if it moved the entire distance if (trace.fraction == 1) @@ -359,7 +435,7 @@ int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal) // run the impact function if (impact) { - SV_Impact(ent, trace.ent); + SV_Impact(ent, &trace); // break if removed by the impact function if (ent->priv.server->free) @@ -453,6 +529,10 @@ int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal) trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent); } */ + + // LordHavoc: this came from QW and allows you to get out of water more easily + if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP)) + VectorCopy(primal_velocity, ent->fields.server->velocity); return blocked; } @@ -491,7 +571,7 @@ SV_PushEntity Does not change the entities velocity at all ============ */ -trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push) +trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonstartsolid) { int type; trace_t trace; @@ -507,12 +587,14 @@ trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push) type = MOVE_NORMAL; trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent); + if (trace.startsolid && failonstartsolid) + return trace; VectorCopy (trace.endpos, ent->fields.server->origin); SV_LinkEdict (ent, true); if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent))) - SV_Impact (ent, trace.ent); + SV_Impact (ent, &trace); return trace; } @@ -523,13 +605,11 @@ SV_PushMove ============ */ -trace_t SV_ClipMoveToEntity (prvm_edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end); void SV_PushMove (prvm_edict_t *pusher, float movetime) { int i, e, index; - prvm_edict_t *check, *ed; float savesolid, movetime2, pushltime; - vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, org2; + vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org; int num_moved; int numcheckentities; static prvm_edict_t *checkentities[MAX_EDICTS]; @@ -562,13 +642,13 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) SV_LinkEdict (pusher, false); return; default: - Con_Printf("SV_PushMove: unrecognized solid type %f\n", pusher->fields.server->solid); + Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid); return; } index = (int) pusher->fields.server->modelindex; if (index < 1 || index >= MAX_MODELS) { - Con_Printf("SV_PushMove: invalid modelindex %f\n", pusher->fields.server->modelindex); + Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex); return; } pushermodel = sv.models[index]; @@ -632,7 +712,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) VectorCopy (pusher->fields.server->angles, pushang); pushltime = pusher->fields.server->ltime; -// move the pusher to it's final position +// move the pusher to its final position VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin); VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles); @@ -647,24 +727,32 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities); for (e = 0;e < numcheckentities;e++) { - check = checkentities[e]; - if (check->fields.server->movetype == MOVETYPE_PUSH - || check->fields.server->movetype == MOVETYPE_NONE + prvm_edict_t *check = checkentities[e]; + if (check->fields.server->movetype == MOVETYPE_NONE + || check->fields.server->movetype == MOVETYPE_PUSH || check->fields.server->movetype == MOVETYPE_FOLLOW || check->fields.server->movetype == MOVETYPE_NOCLIP || check->fields.server->movetype == MOVETYPE_FAKEPUSH) continue; // if the entity is standing on the pusher, it will definitely be moved - if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher)) + if (((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher) + { + // remove the onground flag for non-players + if (check->fields.server->movetype != MOVETYPE_WALK) + check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND; + } + else { // if the entity is not inside the pusher's final position, leave it alone - if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin).startsolid) + if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid) continue; } + if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used { + vec3_t org2; VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org); org2[0] = DotProduct (org, forward); org2[1] = DotProduct (org, left); @@ -675,66 +763,76 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) else VectorCopy (move1, move); - // remove the onground flag for non-players - if (check->fields.server->movetype != MOVETYPE_WALK) - check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND; - VectorCopy (check->fields.server->origin, check->priv.server->moved_from); VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles); sv.moved_edicts[num_moved++] = check; // try moving the contacted entity pusher->fields.server->solid = SOLID_NOT; - trace = SV_PushEntity (check, move); + trace = SV_PushEntity (check, move, true); // FIXME: turn players specially check->fields.server->angles[1] += trace.fraction * moveangle[1]; pusher->fields.server->solid = savesolid; // was SOLID_BSP + //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid); // if it is still inside the pusher, block - if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin).startsolid) + if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid) { // try moving the contacted entity a tiny bit further to account for precision errors + vec3_t move2; pusher->fields.server->solid = SOLID_NOT; - VectorScale(move, 0.1, move); - SV_PushEntity (check, move); + VectorScale(move, 1.1, move2); + VectorCopy (check->priv.server->moved_from, check->fields.server->origin); + VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles); + SV_PushEntity (check, move2, true); pusher->fields.server->solid = savesolid; - if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin).startsolid) + if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid) { - // still inside pusher, so it's really blocked - - // fail the move - if (check->fields.server->mins[0] == check->fields.server->maxs[0]) - continue; - if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER) + // try moving the contacted entity a tiny bit less to account for precision errors + pusher->fields.server->solid = SOLID_NOT; + VectorScale(move, 0.9, move2); + VectorCopy (check->priv.server->moved_from, check->fields.server->origin); + VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles); + SV_PushEntity (check, move2, true); + pusher->fields.server->solid = savesolid; + if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid) { - // corpse - check->fields.server->mins[0] = check->fields.server->mins[1] = 0; - VectorCopy (check->fields.server->mins, check->fields.server->maxs); - continue; - } + // still inside pusher, so it's really blocked - VectorCopy (pushorig, pusher->fields.server->origin); - VectorCopy (pushang, pusher->fields.server->angles); - pusher->fields.server->ltime = pushltime; - SV_LinkEdict (pusher, false); + // fail the move + if (check->fields.server->mins[0] == check->fields.server->maxs[0]) + continue; + if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER) + { + // corpse + check->fields.server->mins[0] = check->fields.server->mins[1] = 0; + VectorCopy (check->fields.server->mins, check->fields.server->maxs); + continue; + } - // move back any entities we already moved - for (i = 0;i < num_moved;i++) - { - ed = sv.moved_edicts[i]; - VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin); - VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles); - SV_LinkEdict (ed, false); - } + VectorCopy (pushorig, pusher->fields.server->origin); + VectorCopy (pushang, pusher->fields.server->angles); + pusher->fields.server->ltime = pushltime; + SV_LinkEdict (pusher, false); - // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone - if (pusher->fields.server->blocked) - { - prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher); - prog->globals.server->other = PRVM_EDICT_TO_PROG(check); - PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing"); + // move back any entities we already moved + for (i = 0;i < num_moved;i++) + { + prvm_edict_t *ed = sv.moved_edicts[i]; + VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin); + VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles); + SV_LinkEdict (ed, false); + } + + // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone + if (pusher->fields.server->blocked) + { + prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher); + prog->globals.server->other = PRVM_EDICT_TO_PROG(check); + PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing"); + } + break; } - break; } } } @@ -811,12 +909,12 @@ void SV_CheckStuck (prvm_edict_t *ent) VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin); if (!SV_TestEntityPosition(ent)) { - Con_DPrint("Unstuck.\n"); + Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname)); SV_LinkEdict (ent, true); return; } - for (z=0 ; z< 18 ; z++) + for (z=-1 ; z< 18 ; z++) for (i=-1 ; i <= 1 ; i++) for (j=-1 ; j <= 1 ; j++) { @@ -825,14 +923,44 @@ void SV_CheckStuck (prvm_edict_t *ent) ent->fields.server->origin[2] = org[2] + z; if (!SV_TestEntityPosition(ent)) { - Con_DPrint("Unstuck.\n"); + Con_DPrintf("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), (float)i, (float)j, (float)z); SV_LinkEdict (ent, true); return; } } VectorCopy (org, ent->fields.server->origin); - Con_DPrint("player is stuck.\n"); + Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname)); +} + +static void SV_UnstickEntity (prvm_edict_t *ent) +{ + int i, j, z; + vec3_t org; + + // if not stuck in a bmodel, just return + if (!SV_TestEntityPosition(ent)) + return; + + VectorCopy (ent->fields.server->origin, org); + + for (z=-1 ; z< 18 ; z += 6) + for (i=-1 ; i <= 1 ; i++) + for (j=-1 ; j <= 1 ; j++) + { + ent->fields.server->origin[0] = org[0] + i; + ent->fields.server->origin[1] = org[1] + j; + ent->fields.server->origin[2] = org[2] + z; + if (!SV_TestEntityPosition(ent)) + { + Con_DPrintf("Unstuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), (float)i, (float)j, (float)z); + SV_LinkEdict (ent, true); + return; + } + } + + VectorCopy (org, ent->fields.server->origin); + Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname)); } @@ -928,7 +1056,7 @@ int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel) case 7: dir[0] = -2; dir[1] = -2; break; } - SV_PushEntity (ent, dir); + SV_PushEntity (ent, dir, false); // retry the original move ent->fields.server->velocity[0] = oldvel[0]; @@ -1019,7 +1147,7 @@ void SV_WalkMove (prvm_edict_t *ent) VectorClear (upmove); upmove[2] = sv_stepheight.value; // FIXME: don't link? - SV_PushEntity(ent, upmove); + SV_PushEntity(ent, upmove, false); // move forward ent->fields.server->velocity[2] = 0; @@ -1060,10 +1188,13 @@ void SV_WalkMove (prvm_edict_t *ent) VectorClear (downmove); downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime; // FIXME: don't link? - downtrace = SV_PushEntity (ent, downmove); + downtrace = SV_PushEntity (ent, downmove, false); if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7) { + // this has been disabled so that you can't jump when you are stepping + // up while already jumping (also known as the Quake2 stair jump bug) +#if 0 // LordHavoc: disabled this check so you can walk on monsters/players //if (ent->fields.server->solid == SOLID_BSP) { @@ -1071,6 +1202,7 @@ void SV_WalkMove (prvm_edict_t *ent) ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND; ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent); } +#endif } else { @@ -1151,7 +1283,7 @@ SV_CheckWaterTransition void SV_CheckWaterTransition (prvm_edict_t *ent) { int cont; - cont = SV_PointQ1Contents(ent->fields.server->origin); + cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin)); if (!ent->fields.server->watertype) { // just spawned here @@ -1161,8 +1293,8 @@ void SV_CheckWaterTransition (prvm_edict_t *ent) } // check if the entity crossed into or out of water - if (gamemode != GAME_NEXUIZ && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME))) - SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1); + if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME))) + SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1); if (cont <= CONTENTS_WATER) { @@ -1188,20 +1320,24 @@ void SV_Physics_Toss (prvm_edict_t *ent) trace_t trace; vec3_t move; - // don't stick to ground if onground and moving upward - if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND)) - ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND; - // if onground, return without moving if ((int)ent->fields.server->flags & FL_ONGROUND) { - if (ent->fields.server->groundentity == 0 || sv_gameplayfix_noairborncorpse.integer) + if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) + { + // don't stick to ground if onground and moving upward + ent->fields.server->flags -= FL_ONGROUND; + } + else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer) + { + // we can trust FL_ONGROUND if groundentity is world because it never moves return; - // if ent was supported by a brush model on previous frame, - // and groundentity is now freed, set groundentity to 0 (floating) - if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free) + } + else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free) { - // leave it suspended in the air + // if ent was supported by a brush model on previous frame, + // and groundentity is now freed, set groundentity to 0 (world) + // which leaves it suspended in the air ent->fields.server->groundentity = 0; return; } @@ -1219,9 +1355,17 @@ void SV_Physics_Toss (prvm_edict_t *ent) // move origin VectorScale (ent->fields.server->velocity, sv.frametime, move); - trace = SV_PushEntity (ent, move); + trace = SV_PushEntity (ent, move, true); if (ent->priv.server->free) return; + if (trace.bmodelstartsolid) + { + // try to unstick the entity + SV_UnstickEntity(ent); + trace = SV_PushEntity (ent, move, false); + if (ent->priv.server->free) + return; + } if (trace.fraction < 1) { @@ -1303,23 +1447,37 @@ will fall if the floor is pulled out from under them. */ void SV_Physics_Step (prvm_edict_t *ent) { - // don't stick to ground if onground and moving upward - if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND)) - ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND; - - // freefall if not onground/fly/swim - if (!((int)ent->fields.server->flags & (FL_ONGROUND | FL_FLY | FL_SWIM))) + int flags = (int)ent->fields.server->flags; + // don't fall at all if fly/swim + if (!(flags & (FL_FLY | FL_SWIM))) { - int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1; + if (flags & FL_ONGROUND) + { + // freefall if onground and moving upward + // freefall if not standing on a world surface (it may be a lift or trap door) + if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity) + { + ent->fields.server->flags -= FL_ONGROUND; + SV_AddGravity(ent); + SV_CheckVelocity(ent); + SV_FlyMove(ent, sv.frametime, NULL); + SV_LinkEdict(ent, true); + } + } + else + { + // freefall if not onground + int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1; - SV_AddGravity(ent); - SV_CheckVelocity(ent); - SV_FlyMove(ent, sv.frametime, NULL); - SV_LinkEdict(ent, true); + SV_AddGravity(ent); + SV_CheckVelocity(ent); + SV_FlyMove(ent, sv.frametime, NULL); + SV_LinkEdict(ent, true); - // just hit ground - if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && gamemode != GAME_NEXUIZ) - SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1); + // just hit ground + if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string) + SV_StartSound(ent, 0, sv_sound_land.string, 255, 1); + } } // regular thinking @@ -1330,38 +1488,92 @@ void SV_Physics_Step (prvm_edict_t *ent) //============================================================================ -void SV_Physics_Entity (prvm_edict_t *ent, qboolean runmove) +static void SV_Physics_Entity (prvm_edict_t *ent) { - int i = ent - prog->edicts; - if (i >= 1 && i <= svs.maxclients) + // don't run a move on newly spawned projectiles as it messes up movement + // interpolation and rocket trails + qboolean runmove = ent->priv.server->move; + ent->priv.server->move = true; + switch ((int) ent->fields.server->movetype) { - // make sure the velocity is sane (not a NaN) - SV_CheckVelocity(ent); - // LordHavoc: QuakeC replacement for SV_ClientThink (player movement) - if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer) + case MOVETYPE_PUSH: + case MOVETYPE_FAKEPUSH: + SV_Physics_Pusher (ent); + break; + case MOVETYPE_NONE: + // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects + if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime) + SV_RunThink (ent); + break; + case MOVETYPE_FOLLOW: + SV_Physics_Follow (ent); + break; + case MOVETYPE_NOCLIP: + if (SV_RunThink(ent)) { - prog->globals.server->time = sv.time; - prog->globals.server->self = PRVM_EDICT_TO_PROG(ent); - PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing"); + SV_CheckWater(ent); + VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin); + VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles); } - else - SV_ClientThink (); - // make sure the velocity is sane (not a NaN) - SV_CheckVelocity(ent); - // LordHavoc: a hack to ensure that the (rather silly) id1 quakec - // player_run/player_stand1 does not horribly malfunction if the - // velocity becomes a number that is both == 0 and != 0 - // (sounds to me like NaN but to be absolutely safe...) - if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001) - VectorClear(ent->fields.server->velocity); - // call standard client pre-think + SV_LinkEdict(ent, false); + break; + case MOVETYPE_STEP: + SV_Physics_Step (ent); + break; + case MOVETYPE_WALK: + if (SV_RunThink (ent)) + { + if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) ) + SV_AddGravity (ent); + SV_CheckStuck (ent); + SV_WalkMove (ent); + SV_LinkEdict (ent, true); + } + break; + case MOVETYPE_TOSS: + case MOVETYPE_BOUNCE: + case MOVETYPE_BOUNCEMISSILE: + case MOVETYPE_FLYMISSILE: + case MOVETYPE_FLY: + // regular thinking + if (SV_RunThink (ent) && runmove) + SV_Physics_Toss (ent); + break; + default: + Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype); + break; + } +} + +void SV_ApplyClientMove (void); +void SV_Physics_ClientEntity (prvm_edict_t *ent) +{ + SV_ApplyClientMove(); + // make sure the velocity is sane (not a NaN) + SV_CheckVelocity(ent); + // LordHavoc: QuakeC replacement for SV_ClientThink (player movement) + if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer) + { prog->globals.server->time = sv.time; prog->globals.server->self = PRVM_EDICT_TO_PROG(ent); - PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing"); - SV_CheckVelocity (ent); + PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing"); } + else + SV_ClientThink (); + // make sure the velocity is sane (not a NaN) + SV_CheckVelocity(ent); + // LordHavoc: a hack to ensure that the (rather silly) id1 quakec + // player_run/player_stand1 does not horribly malfunction if the + // velocity becomes a number that is both == 0 and != 0 + // (sounds to me like NaN but to be absolutely safe...) + if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001) + VectorClear(ent->fields.server->velocity); + // call standard client pre-think + prog->globals.server->time = sv.time; + prog->globals.server->self = PRVM_EDICT_TO_PROG(ent); + PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing"); + SV_CheckVelocity (ent); - // LordHavoc: merged client and normal entity physics switch ((int) ent->fields.server->movetype) { case MOVETYPE_PUSH: @@ -1383,9 +1595,6 @@ void SV_Physics_Entity (prvm_edict_t *ent, qboolean runmove) VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin); VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles); } - // relink normal entities here, players always get relinked so don't relink twice - if (!(i > 0 && i <= svs.maxclients)) - SV_LinkEdict(ent, false); break; case MOVETYPE_STEP: SV_Physics_Step (ent); @@ -1397,9 +1606,6 @@ void SV_Physics_Entity (prvm_edict_t *ent, qboolean runmove) SV_AddGravity (ent); SV_CheckStuck (ent); SV_WalkMove (ent); - // relink normal entities here, players always get relinked so don't relink twice - if (!(i > 0 && i <= svs.maxclients)) - SV_LinkEdict (ent, true); } break; case MOVETYPE_TOSS: @@ -1407,42 +1613,33 @@ void SV_Physics_Entity (prvm_edict_t *ent, qboolean runmove) case MOVETYPE_BOUNCEMISSILE: case MOVETYPE_FLYMISSILE: // regular thinking - if (SV_RunThink (ent) && runmove) + if (SV_RunThink (ent)) SV_Physics_Toss (ent); break; case MOVETYPE_FLY: - if (SV_RunThink (ent) && runmove) + if (SV_RunThink (ent)) { - if (i > 0 && i <= svs.maxclients) - { - SV_CheckWater (ent); - SV_WalkMove (ent); - } - else - SV_Physics_Toss (ent); + SV_CheckWater (ent); + SV_WalkMove (ent); } break; default: - Con_Printf ("SV_Physics: bad movetype %i", (int)ent->fields.server->movetype); + Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype); break; } - if (i >= 1 && i <= svs.maxclients) - { - SV_CheckVelocity (ent); + SV_CheckVelocity (ent); - // call standard player post-think - SV_LinkEdict (ent, true); + // call standard player post-think + SV_LinkEdict (ent, true); - SV_CheckVelocity (ent); + SV_CheckVelocity (ent); - prog->globals.server->time = sv.time; - prog->globals.server->self = PRVM_EDICT_TO_PROG(ent); - PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing"); - } + prog->globals.server->time = sv.time; + prog->globals.server->self = PRVM_EDICT_TO_PROG(ent); + PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing"); } - /* ================ SV_Physics @@ -1451,9 +1648,8 @@ SV_Physics */ void SV_Physics (void) { - int i, newnum_edicts; + int i; prvm_edict_t *ent; - qbyte runmove[MAX_EDICTS]; // let the progs know that a new frame has started prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts); @@ -1462,43 +1658,38 @@ void SV_Physics (void) prog->globals.server->frametime = sv.frametime; PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing"); - newnum_edicts = 0; - for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent)) - if ((runmove[i] = !ent->priv.server->free)) - newnum_edicts = i + 1; - prog->num_edicts = max(svs.maxclients + 1, newnum_edicts); - // // treat each object in turn // - for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent)) - { - if (ent->priv.server->free) - continue; - - if (prog->globals.server->force_retouch) - SV_LinkEdict (ent, true); // force retouch even for stationary + // if force_retouch, relink all the entities + if (prog->globals.server->force_retouch > 0) + for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent)) + if (!ent->priv.server->free) + SV_LinkEdict (ent, true); // force retouch even for stationary - if (i >= 1 && i <= svs.maxclients) + // run physics on the client entities + for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++) + { + if (!ent->priv.server->free) { - host_client = svs.clients + i - 1; // don't do physics on disconnected clients, FrikBot relies on this if (!host_client->spawned) - { memset(&host_client->cmd, 0, sizeof(host_client->cmd)); - continue; - } - // connected slot - if (host_client->movesequence) - continue; // return if running asynchronously + // don't run physics here if running asynchronously + else if (host_client->clmovement_skipphysicsframes > 0) + host_client->clmovement_skipphysicsframes--; + else + SV_Physics_ClientEntity(ent); } - else if (sv_freezenonclients.integer) - continue; - - SV_Physics_Entity(ent, runmove[i]); } + // run physics on all the non-client entities + if (!sv_freezenonclients.integer) + for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent)) + if (!ent->priv.server->free) + SV_Physics_Entity(ent); + if (prog->globals.server->force_retouch > 0) prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1); @@ -1511,6 +1702,9 @@ void SV_Physics (void) PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing"); } + // decrement prog->num_edicts if the highest number entities died + for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--); + if (!sv_freezenonclients.integer) sv.time += sv.frametime; } @@ -1519,23 +1713,20 @@ void SV_Physics (void) trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore) { int i; - float gravity, savesolid; + float gravity; vec3_t move, end; - prvm_edict_t tempent, *tent; - entvars_t vars; + vec3_t original_origin; + vec3_t original_velocity; + vec3_t original_angles; + vec3_t original_avelocity; prvm_eval_t *val; trace_t trace; - // copy the vars over - memcpy(&vars, tossent->fields.server, sizeof(entvars_t)); - // set up the temp entity to point to the copied vars - tent = &tempent; - tent->fields.server = &vars; + VectorCopy(tossent->fields.server->origin , original_origin ); + VectorCopy(tossent->fields.server->velocity , original_velocity ); + VectorCopy(tossent->fields.server->angles , original_angles ); + VectorCopy(tossent->fields.server->avelocity, original_avelocity); - savesolid = tossent->fields.server->solid; - tossent->fields.server->solid = SOLID_NOT; - - // this has to fetch the field from the original edict, since our copy is truncated val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity); if (val != NULL && val->_float != 0) gravity = val->_float; @@ -1545,19 +1736,23 @@ trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore) for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds { - SV_CheckVelocity (tent); - tent->fields.server->velocity[2] -= gravity; - VectorMA (tent->fields.server->angles, 0.05, tent->fields.server->avelocity, tent->fields.server->angles); - VectorScale (tent->fields.server->velocity, 0.05, move); - VectorAdd (tent->fields.server->origin, move, end); - trace = SV_Move (tent->fields.server->origin, tent->fields.server->mins, tent->fields.server->maxs, end, MOVE_NORMAL, tent); - VectorCopy (trace.endpos, tent->fields.server->origin); - - if (trace.fraction < 1 && trace.ent && trace.ent != ignore) + SV_CheckVelocity (tossent); + tossent->fields.server->velocity[2] -= gravity; + VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles); + VectorScale (tossent->fields.server->velocity, 0.05, move); + VectorAdd (tossent->fields.server->origin, move, end); + trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent); + VectorCopy (trace.endpos, tossent->fields.server->origin); + + if (trace.fraction < 1) break; } - tossent->fields.server->solid = savesolid; - trace.fraction = 0; // not relevant + + VectorCopy(original_origin , tossent->fields.server->origin ); + VectorCopy(original_velocity , tossent->fields.server->velocity ); + VectorCopy(original_angles , tossent->fields.server->angles ); + VectorCopy(original_avelocity, tossent->fields.server->avelocity); + return trace; }