X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=sv_phys.c;h=3a4a8b005e6e94bb66c847999b3eb47f99f5a26a;hp=e7e5d010443a0816fd30336014bcfdff94da67fb;hb=ad3882107f0d4c034ede6b13ba15a23086be4f6d;hpb=e8ca8a5fd19210b5605c207a04c6dbb8bebe5ef5 diff --git a/sv_phys.c b/sv_phys.c index e7e5d010..3a4a8b00 100644 --- a/sv_phys.c +++ b/sv_phys.c @@ -20,8 +20,6 @@ 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" /* @@ -41,43 +39,385 @@ solid_edge items only clip against bsp models. */ -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 void SV_Physics_Toss (prvm_edict_t *ent); -void SV_Phys_Init (void) +/* +=============================================================================== + +LINE TESTING IN HULLS + +=============================================================================== +*/ + +int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict) +{ + prvm_eval_t *val; + if (passedict) + { + val = PRVM_EDICTFIELDVALUE(passedict, prog->fieldoffsets.dphitcontentsmask); + if (val && val->_float) + return (int)val->_float; + else if (passedict->fields.server->solid == SOLID_SLIDEBOX) + { + if ((int)passedict->fields.server->flags & FL_MONSTER) + return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP; + else + return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP; + } + else if (passedict->fields.server->solid == SOLID_CORPSE) + return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY; + else if (passedict->fields.server->solid == SOLID_TRIGGER) + return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY; + else + return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE; + } + else + return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE; +} + +/* +================== +SV_Move +================== +*/ +#if COLLISIONPARANOID >= 1 +trace_t SV_Move_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask) +#else +trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask) +#endif +{ + vec3_t hullmins, hullmaxs; + int i, bodysupercontents; + int passedictprog; + qboolean pointtrace; + prvm_edict_t *traceowner, *touch; + trace_t trace; + // bounding box of entire move area + vec3_t clipboxmins, clipboxmaxs; + // size of the moving object + vec3_t clipmins, clipmaxs; + // size when clipping against monsters + vec3_t clipmins2, clipmaxs2; + // start and end origin of move + vec3_t clipstart, clipend; + // trace results + trace_t cliptrace; + // matrices to transform into/out of other entity's space + matrix4x4_t matrix, imatrix; + // model of other entity + model_t *model; + // list of entities to test for collisions + int numtouchedicts; + prvm_edict_t *touchedicts[MAX_EDICTS]; + + VectorCopy(start, clipstart); + VectorCopy(end, clipend); + VectorCopy(mins, clipmins); + VectorCopy(maxs, clipmaxs); + VectorCopy(mins, clipmins2); + VectorCopy(maxs, clipmaxs2); +#if COLLISIONPARANOID >= 3 + Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]); +#endif + + // clip to world + Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask); + cliptrace.bmodelstartsolid = cliptrace.startsolid; + if (cliptrace.startsolid || cliptrace.fraction < 1) + cliptrace.ent = prog->edicts; + if (type == MOVE_WORLDONLY) + return cliptrace; + + if (type == MOVE_MISSILE) + { + // LordHavoc: modified this, was = -15, now -= 15 + for (i = 0;i < 3;i++) + { + clipmins2[i] -= 15; + clipmaxs2[i] += 15; + } + } + + // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp + if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize) + sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs); + else + { + VectorCopy(clipmins, hullmins); + VectorCopy(clipmaxs, hullmaxs); + } + + // create the bounding box of the entire move + for (i = 0;i < 3;i++) + { + clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1; + clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1; + } + + // debug override to test against everything + if (sv_debugmove.integer) + { + clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999; + clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999; + } + + // if the passedict is world, make it NULL (to avoid two checks each time) + if (passedict == prog->edicts) + passedict = NULL; + // precalculate prog value for passedict for comparisons + passedictprog = PRVM_EDICT_TO_PROG(passedict); + // figure out whether this is a point trace for comparisons + pointtrace = VectorCompare(clipmins, clipmaxs); + // precalculate passedict's owner edict pointer for comparisons + traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0; + + // clip to entities + // because this uses World_EntitiestoBox, we know all entity boxes overlap + // the clip region, so we can skip culling checks in the loop below + numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts); + if (numtouchedicts > MAX_EDICTS) + { + // this never happens + Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS); + numtouchedicts = MAX_EDICTS; + } + for (i = 0;i < numtouchedicts;i++) + { + touch = touchedicts[i]; + + if (touch->fields.server->solid < SOLID_BBOX) + continue; + if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP) + continue; + + if (passedict) + { + // don't clip against self + if (passedict == touch) + continue; + // don't clip owned entities against owner + if (traceowner == touch) + continue; + // don't clip owner against owned entities + if (passedictprog == touch->fields.server->owner) + continue; + // don't clip points against points (they can't collide) + if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER))) + continue; + } + + bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY; + + // might interact, so do an exact clip + model = NULL; + if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL) + { + unsigned int modelindex = (unsigned int)touch->fields.server->modelindex; + // if the modelindex is 0, it shouldn't be SOLID_BSP! + if (modelindex > 0 && modelindex < MAX_MODELS) + model = sv.models[(int)touch->fields.server->modelindex]; + } + if (model) + Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1); + else + Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]); + Matrix4x4_Invert_Simple(&imatrix, &matrix); + if ((int)touch->fields.server->flags & FL_MONSTER) + Collision_ClipToGenericEntity(&trace, model, touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask); + else + Collision_ClipToGenericEntity(&trace, model, touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask); + + Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP); + } + + return cliptrace; +} + +#if COLLISIONPARANOID >= 1 +trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask) +{ + int endstuck; + trace_t trace; + vec3_t temp; + trace = SV_Move_(start, mins, maxs, end, type, passedict, hitsupercontentsmask); + if (passedict) + { + VectorCopy(trace.endpos, temp); + endstuck = SV_Move_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid; +#if COLLISIONPARANOID < 3 + if (trace.startsolid || endstuck) +#endif + Con_Printf("%s{e%i:%f %f %f:%f %f %f:%f:%f %f %f%s%s}\n", (trace.startsolid || endstuck) ? "^3" : "", passedict ? (int)(passedict - prog->edicts) : -1, passedict->fields.server->origin[0], passedict->fields.server->origin[1], passedict->fields.server->origin[2], end[0] - passedict->fields.server->origin[0], end[1] - passedict->fields.server->origin[1], end[2] - passedict->fields.server->origin[2], trace.fraction, trace.endpos[0] - passedict->fields.server->origin[0], trace.endpos[1] - passedict->fields.server->origin[1], trace.endpos[2] - passedict->fields.server->origin[2], trace.startsolid ? " startstuck" : "", endstuck ? " endstuck" : ""); + } + return trace; +} +#endif + +/* +=============================================================================== + +Linking entities into the world culling system + +=============================================================================== +*/ + +void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent) +{ + int i, numtouchedicts, old_self, old_other; + prvm_edict_t *touch, *touchedicts[MAX_EDICTS]; + + // build a list of edicts to touch, because the link loop can be corrupted + // by IncreaseEdicts called during touch functions + numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts); + if (numtouchedicts > MAX_EDICTS) + { + // this never happens + Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS); + numtouchedicts = MAX_EDICTS; + } + + old_self = prog->globals.server->self; + old_other = prog->globals.server->other; + for (i = 0;i < numtouchedicts;i++) + { + touch = touchedicts[i]; + if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch) + { + prvm_eval_t *val; + prog->globals.server->self = PRVM_EDICT_TO_PROG(touch); + prog->globals.server->other = PRVM_EDICT_TO_PROG(ent); + prog->globals.server->time = sv.time; + 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 (touch->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(ent); + if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents))) + val->_float = 0; + if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents))) + val->_float = 0; + if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags))) + val->_float = 0; + if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename))) + val->string = 0; + PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing"); + } + } + prog->globals.server->self = old_self; + prog->globals.server->other = old_other; +} + +/* +=============== +SV_LinkEdict + +=============== +*/ +void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers) { - Cvar_RegisterVariable(&sv_stepheight); - Cvar_RegisterVariable(&sv_jumpstep); - Cvar_RegisterVariable(&sv_wallfriction); - Cvar_RegisterVariable(&sv_newflymove); - Cvar_RegisterVariable(&sv_freezenonclients); + model_t *model; + vec3_t mins, maxs; + + if (ent == prog->edicts) + return; // don't add the world + + if (ent->priv.server->free) + return; + +// set the abs box + + if (ent->fields.server->solid == SOLID_BSP) + { + int modelindex = (int)ent->fields.server->modelindex; + if (modelindex < 0 || modelindex > MAX_MODELS) + { + Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent)); + modelindex = 0; + } + model = sv.models[modelindex]; + if (model != NULL) + { + if (!model->TraceBox) + Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent)); + + if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2]) + { + VectorAdd(ent->fields.server->origin, model->rotatedmins, mins); + VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs); + } + else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1]) + { + VectorAdd(ent->fields.server->origin, model->yawmins, mins); + VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs); + } + else + { + VectorAdd(ent->fields.server->origin, model->normalmins, mins); + VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs); + } + } + else + { + // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily + VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins); + VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs); + } + } + else + { + VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins); + VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs); + } + +// +// to make items easier to pick up and allow them to be grabbed off +// of shelves, the abs sizes are expanded +// + if ((int)ent->fields.server->flags & FL_ITEM) + { + mins[0] -= 15; + mins[1] -= 15; + mins[2] -= 1; + maxs[0] += 15; + maxs[1] += 15; + maxs[2] += 1; + } + else + { + // because movement is clipped an epsilon away from an actual edge, + // we must fully check even when bounding boxes don't quite touch + mins[0] -= 1; + mins[1] -= 1; + mins[2] -= 1; + maxs[0] += 1; + maxs[1] += 1; + maxs[2] += 1; + } + + VectorCopy(mins, ent->fields.server->absmin); + VectorCopy(maxs, ent->fields.server->absmax); - Cvar_RegisterVariable(&sv_playerphysicsqc); + World_LinkEdict(&sv.world, ent, mins, maxs); - Cvar_RegisterVariable(&sv_sound_watersplash); - Cvar_RegisterVariable(&sv_sound_land); + // if touch_triggers, call touch on all entities overlapping this box + if (touch_triggers && ent->fields.server->solid != SOLID_NOT) + SV_LinkEdict_TouchAreaGrid(ent); } +/* +=============================================================================== + +Utility functions + +=============================================================================== +*/ + /* ============ SV_TestEntityPosition @@ -85,31 +425,45 @@ SV_TestEntityPosition returns true if the entity is in solid currently ============ */ -static int SV_TestEntityPosition (prvm_edict_t *ent) +static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset) { - trace_t trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent); + vec3_t org; + trace_t trace; + VectorAdd(ent->fields.server->origin, offset, org); + trace = SV_Move (org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, SUPERCONTENTS_SOLID); if (trace.startsupercontents & SUPERCONTENTS_SOLID) return true; else { - if (sv.worldmodel->brushq1.hulls && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs)) + if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs)) { // q1bsp/hlbsp use hulls and if the entity does not exactly match // a hull size it is incorrectly tested, so this code tries to // 'fix' it slightly... + // FIXME: this breaks entities larger than the hull size int i; - vec3_t v; + vec3_t v, m1, m2, s; + VectorAdd(org, ent->fields.server->mins, m1); + VectorAdd(org, ent->fields.server->maxs, m2); + VectorSubtract(m2, m1, s); +#define EPSILON (1.0f / 32.0f) + if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;} + if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;} + if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;} for (i = 0;i < 8;i++) { - v[0] = (i & 1) ? ent->fields.server->maxs[0] : ent->fields.server->mins[0]; - v[1] = (i & 2) ? ent->fields.server->maxs[1] : ent->fields.server->mins[1]; - v[2] = (i & 4) ? ent->fields.server->maxs[2] : ent->fields.server->mins[2]; + v[0] = (i & 1) ? m2[0] : m1[0]; + v[1] = (i & 2) ? m2[1] : m1[1]; + v[2] = (i & 4) ? m2[2] : m1[2]; if (SV_PointSuperContents(v) & SUPERCONTENTS_SOLID) return true; } } - return false; } + // if the trace found a better position for the entity, move it there + if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001) + VectorCopy(trace.endpos, ent->fields.server->origin); + return false; } /* @@ -134,7 +488,7 @@ void SV_CheckAllEnts (void) || check->fields.server->movetype == MOVETYPE_NOCLIP) continue; - if (SV_TestEntityPosition (check)) + if (SV_TestEntityPosition (check, vec3_origin)) Con_Print("entity in invalid position\n"); } } @@ -158,7 +512,7 @@ int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents) if(ent->fields.server->watertype != nContents) { // Changed Contents // Acquire Contents Transition Function from QC - contentstransition = PRVM_GETEDICTFIELDVALUE(ent, eval_contentstransition); + contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition); if(contentstransition->function) { // Valid Function; Execute @@ -229,22 +583,27 @@ Returns false if the entity removed itself. */ qboolean SV_RunThink (prvm_edict_t *ent) { - float thinktime; - - thinktime = ent->fields.server->nextthink; - if (thinktime <= 0 || thinktime > sv.time + sv.frametime) - return true; + int iterations; // don't let things stay in the past. // it is possible to start that way by a trigger with a local time. - if (thinktime < sv.time) - thinktime = sv.time; + if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime) + return true; - ent->fields.server->nextthink = 0; - prog->globals.server->time = thinktime; - prog->globals.server->self = PRVM_EDICT_TO_PROG(ent); - prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts); - PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing"); + for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++) + { + prog->globals.server->time = max(sv.time, ent->fields.server->nextthink); + ent->fields.server->nextthink = 0; + prog->globals.server->self = PRVM_EDICT_TO_PROG(ent); + prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts); + PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing"); + // mods often set nextthink to time to cause a think every frame, + // we don't want to loop in that case, so exit if the new nextthink is + // <= the time the qc was told, also exit if it is past the end of the + // frame + if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer) + break; + } return !ent->priv.server->free; } @@ -255,49 +614,26 @@ SV_Impact Two entities have touched, so run their touch functions ================== */ +extern void VM_SetTraceGlobals(const trace_t *trace); +extern sizebuf_t vm_tempstringsbuf; void SV_Impact (prvm_edict_t *e1, trace_t *trace) { + int restorevm_tempstringsbuf_cursize; 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; + restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize; + + VM_SetTraceGlobals(trace); prog->globals.server->time = sv.time; 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"); } @@ -305,28 +641,24 @@ void SV_Impact (prvm_edict_t *e1, trace_t *trace) { 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; + VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos); + VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal); + prog->globals.server->trace_plane_dist = -trace->plane.dist; prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1); - if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents))) + if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents))) val->_float = 0; - if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents))) + if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents))) val->_float = 0; - if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags))) + if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags))) val->_float = 0; - if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename))) + if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename))) val->string = 0; PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing"); } prog->globals.server->self = old_self; prog->globals.server->other = old_other; + vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize; } @@ -367,13 +699,15 @@ If stepnormal is not NULL, the plane normal of any vertical wall hit will be sto */ // LordHavoc: increased from 5 to 32 #define MAX_CLIP_PLANES 32 -int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal) +int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal, int hitsupercontentsmask) { int blocked, bumpcount; int i, j, impact, numplanes; float d, time_left; vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity; trace_t trace; + if (time <= 0) + return 0; blocked = 0; VectorCopy(ent->fields.server->velocity, original_velocity); VectorCopy(ent->fields.server->velocity, primal_velocity); @@ -385,7 +719,7 @@ int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal) break; VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end); - trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent); + trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask); #if 0 //if (trace.fraction < 0.002) { @@ -396,7 +730,7 @@ int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal) start[2] += 3;//0.03125; VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end); end[2] += 3;//0.03125; - testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent); + testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask); if (trace.fraction < testtrace.fraction && !testtrace.startsolid && (testtrace.fraction == 1 || DotProduct(trace.plane.normal, ent->fields.server->velocity) < DotProduct(testtrace.plane.normal, ent->fields.server->velocity))) { Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction); @@ -411,7 +745,7 @@ int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal) VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end); VectorMA(start, 3, planes[i], start); VectorMA(end, 3, planes[i], end); - testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent); + testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask); if (trace.fraction < testtrace.fraction) { trace = testtrace; @@ -457,13 +791,7 @@ int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal) trace.ent = prog->edicts; } - if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent)) - impact = false; - else - { - ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND; - impact = true; - } + impact = !((int) ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent); if (trace.plane.normal[2]) { @@ -586,7 +914,7 @@ int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal) { // LordHavoc: fix the 'fall to your death in a wedge corner' glitch // flag ONGROUND if there's ground under it - trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent); + trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask); } */ @@ -607,7 +935,7 @@ void SV_AddGravity (prvm_edict_t *ent) float ent_gravity; prvm_eval_t *val; - val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity); + val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity); if (val!=0 && val->_float) ent_gravity = val->_float; else @@ -646,7 +974,7 @@ static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmo else type = MOVE_NORMAL; - trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent); + trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent)); if (trace.bmodelstartsolid && failonbmodelstartsolid) return trace; @@ -675,6 +1003,8 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) static prvm_edict_t *checkentities[MAX_EDICTS]; model_t *pushermodel; trace_t trace; + matrix4x4_t pusherfinalmatrix, pusherfinalimatrix; + unsigned short moved_edicts[MAX_EDICTS]; if (!pusher->fields.server->velocity[0] && !pusher->fields.server->velocity[1] && !pusher->fields.server->velocity[2] && !pusher->fields.server->avelocity[0] && !pusher->fields.server->avelocity[1] && !pusher->fields.server->avelocity[2]) { @@ -779,15 +1109,22 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) pusher->fields.server->ltime += movetime; SV_LinkEdict (pusher, false); + pushermodel = NULL; + if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS) + pushermodel = sv.models[(int)pusher->fields.server->modelindex]; + Matrix4x4_CreateFromQuakeEntity(&pusherfinalmatrix, pusher->fields.server->origin[0], pusher->fields.server->origin[1], pusher->fields.server->origin[2], pusher->fields.server->angles[0], pusher->fields.server->angles[1], pusher->fields.server->angles[2], 1); + Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix); + savesolid = pusher->fields.server->solid; // see if any solid entities are inside the final position num_moved = 0; - numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities); + numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities); for (e = 0;e < numcheckentities;e++) { prvm_edict_t *check = checkentities[e]; + int checkcontents = SV_GenericHitSuperContentsMask(check); if (check->fields.server->movetype == MOVETYPE_NONE || check->fields.server->movetype == MOVETYPE_PUSH || check->fields.server->movetype == MOVETYPE_FOLLOW @@ -796,16 +1133,12 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) 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) - { - // 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 standing on the pusher, but is in the pusher's + // final position, move it + if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher) { - // 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, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid) + Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents); + if (!trace.startsolid) continue; } @@ -825,7 +1158,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) 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; + moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check); // try moving the contacted entity pusher->fields.server->solid = SOLID_NOT; @@ -835,8 +1168,15 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) 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); + // this trace.fraction < 1 check causes items to fall off of pushers + // if they pass under or through a wall + // the groundentity check causes items to fall off of ledges + if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)) + check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND; + // 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, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid) + Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents); + if (trace.startsolid) { // try moving the contacted entity a tiny bit further to account for precision errors vec3_t move2; @@ -846,7 +1186,8 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) 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) + Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents); + if (trace.startsolid) { // try moving the contacted entity a tiny bit less to account for precision errors pusher->fields.server->solid = SOLID_NOT; @@ -855,7 +1196,8 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) 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) + Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents); + if (trace.startsolid) { // still inside pusher, so it's really blocked @@ -878,7 +1220,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) // move back any entities we already moved for (i = 0;i < num_moved;i++) { - prvm_edict_t *ed = sv.moved_edicts[i]; + prvm_edict_t *ed = PRVM_EDICT_NUM(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); @@ -946,6 +1288,36 @@ CLIENT MOVEMENT =============================================================================== */ +static float unstickoffsets[] = +{ + -1, 0, 0, + 1, 0, 0, + 0, -1, 0, + 0, 1, 0, + -1, -1, 0, + 1, -1, 0, + -1, 1, 0, + 1, 1, 0, + 0, 0, -1, + 0, 0, 1, + 0, 0, 2, + 0, 0, 3, + 0, 0, 4, + 0, 0, 5, + 0, 0, 6, + 0, 0, 7, + 0, 0, 8, + 0, 0, 9, + 0, 0, 10, + 0, 0, 11, + 0, 0, 12, + 0, 0, 13, + 0, 0, 14, + 0, 0, 15, + 0, 0, 16, + 0, 0, 17, +}; + /* ============= SV_CheckStuck @@ -956,70 +1328,54 @@ clipping hull. */ void SV_CheckStuck (prvm_edict_t *ent) { - int i, j, z; - vec3_t org; + int i; + vec3_t offset; - if (!SV_TestEntityPosition(ent)) + if (!SV_TestEntityPosition(ent, vec3_origin)) { VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin); return; } - VectorCopy (ent->fields.server->origin, org); - VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin); - if (!SV_TestEntityPosition(ent)) + for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3) + { + if (!SV_TestEntityPosition(ent, unstickoffsets + i)) + { + 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), unstickoffsets[i+0], unstickoffsets[i+1], unstickoffsets[i+2]); + SV_LinkEdict (ent, true); + return; + } + } + + VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset); + if (!SV_TestEntityPosition(ent, offset)) { 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=-1 ; z< 18 ; z++) - 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 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_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; + int i; // if not stuck in a bmodel, just return - if (!SV_TestEntityPosition(ent)) + if (!SV_TestEntityPosition(ent, vec3_origin)) 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; - } - } + for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3) + { + if (!SV_TestEntityPosition(ent, unstickoffsets + i)) + { + 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), unstickoffsets[i+0], unstickoffsets[i+1], unstickoffsets[i+2]); + SV_LinkEdict (ent, true); + return; + } + } - VectorCopy (org, ent->fields.server->origin); if (developer.integer >= 100) Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname)); } @@ -1098,6 +1454,7 @@ void SV_WallFriction (prvm_edict_t *ent, float *stepnormal) } } +#if 0 /* ===================== SV_TryUnstick @@ -1139,7 +1496,7 @@ int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel) ent->fields.server->velocity[0] = oldvel[0]; ent->fields.server->velocity[1] = oldvel[1]; ent->fields.server->velocity[2] = 0; - clip = SV_FlyMove (ent, 0.1, NULL); + clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent)); if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4) @@ -1157,6 +1514,7 @@ int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel) Con_DPrint("TryUnstick - failure.\n"); return 7; } +#endif /* ===================== @@ -1167,20 +1525,30 @@ Only used by players */ void SV_WalkMove (prvm_edict_t *ent) { - int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity; + int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask; vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity; trace_t downtrace; + // if frametime is 0 (due to client sending the same timestamp twice), + // don't move + if (sv.frametime <= 0) + return; + + hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent); + SV_CheckVelocity(ent); // do a regular slide move unless it looks like you ran into a step oldonground = (int)ent->fields.server->flags & FL_ONGROUND; - ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND; VectorCopy (ent->fields.server->origin, start_origin); VectorCopy (ent->fields.server->velocity, start_velocity); - clip = SV_FlyMove (ent, sv.frametime, NULL); + clip = SV_FlyMove (ent, sv.frametime, NULL, hitsupercontentsmask); + + // if the move did not hit the ground at any point, we're not on ground + if (!(clip & 1)) + ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND; SV_CheckVelocity(ent); @@ -1228,7 +1596,7 @@ void SV_WalkMove (prvm_edict_t *ent) // move forward ent->fields.server->velocity[2] = 0; - clip = SV_FlyMove (ent, sv.frametime, stepnormal); + clip = SV_FlyMove (ent, sv.frametime, stepnormal, hitsupercontentsmask); ent->fields.server->velocity[2] += start_velocity[2]; SV_CheckVelocity(ent); @@ -1257,8 +1625,8 @@ void SV_WalkMove (prvm_edict_t *ent) if (clip & 2 && sv_wallfriction.integer) SV_WallFriction (ent, stepnormal); } - // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground - else if (!(sv_gameplayfix_stepdown.integer && ent->fields.server->waterlevel < 2 && start_velocity[2] < (1.0 / 32.0) && oldonground && !((int)ent->fields.server->flags & FL_ONGROUND))) + // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground + else if (!sv_gameplayfix_stepdown.integer || ent->fields.server->waterlevel >= 3 || start_velocity[2] >= (1.0 / 32.0) || !oldonground || ((int)ent->fields.server->flags & FL_ONGROUND)) return; // move down @@ -1270,7 +1638,7 @@ void SV_WalkMove (prvm_edict_t *ent) 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) + // up while already jumping (also known as the Quake2 double jump bug) #if 0 // LordHavoc: disabled this check so you can walk on monsters/players //if (ent->fields.server->solid == SOLID_BSP) @@ -1544,7 +1912,7 @@ void SV_Physics_Step (prvm_edict_t *ent) ent->fields.server->flags -= FL_ONGROUND; SV_AddGravity(ent); SV_CheckVelocity(ent); - SV_FlyMove(ent, sv.frametime, NULL); + SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent)); SV_LinkEdict(ent, true); } } @@ -1555,7 +1923,7 @@ void SV_Physics_Step (prvm_edict_t *ent) SV_AddGravity(ent); SV_CheckVelocity(ent); - SV_FlyMove(ent, sv.frametime, NULL); + SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent)); SV_LinkEdict(ent, true); // just hit ground @@ -1629,21 +1997,75 @@ static void SV_Physics_Entity (prvm_edict_t *ent) } } -void SV_ApplyClientMove (void); -void SV_Physics_ClientEntity (prvm_edict_t *ent) +void SV_Physics_ClientMove(void) { - SV_ApplyClientMove(); + prvm_edict_t *ent; + ent = host_client->edict; + + // call player physics, this needs the proper frametime + prog->globals.server->frametime = sv.frametime; + SV_ClientThink(); + + // call standard client pre-think, with frametime = 0 + prog->globals.server->time = sv.time; + prog->globals.server->frametime = 0; + prog->globals.server->self = PRVM_EDICT_TO_PROG(ent); + PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing"); + prog->globals.server->frametime = sv.frametime; + // 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) + // 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); + + // perform MOVETYPE_WALK behavior + if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) ) + SV_AddGravity (ent); + SV_CheckStuck (ent); + SV_WalkMove (ent); + + SV_CheckVelocity (ent); + + SV_LinkEdict (ent, true); + + SV_CheckVelocity (ent); + + // call standard player post-think, with frametime = 0 + prog->globals.server->time = sv.time; + prog->globals.server->frametime = 0; + prog->globals.server->self = PRVM_EDICT_TO_PROG(ent); + PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing"); + prog->globals.server->frametime = sv.frametime; + + if(ent->fields.server->fixangle) { - 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"); + // angle fixing was requested by physics code... + // so store the current angles for later use + memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles)); + host_client->fixangle_angles_set = TRUE; + + // and clear fixangle for the next frame + ent->fields.server->fixangle = 0; } - else - SV_ClientThink (); +} + +void SV_Physics_ClientEntity(prvm_edict_t *ent) +{ + // don't do physics on disconnected clients, FrikBot relies on this + if (!host_client->spawned) + { + memset(&host_client->cmd, 0, sizeof(host_client->cmd)); + return; + } + + // don't run physics here if running asynchronously + if (host_client->clmovement_skipphysicsframes <= 0) + 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 @@ -1652,6 +2074,7 @@ void SV_Physics_ClientEntity (prvm_edict_t *ent) // (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); @@ -1673,18 +2096,18 @@ void SV_Physics_ClientEntity (prvm_edict_t *ent) SV_Physics_Follow (ent); break; case MOVETYPE_NOCLIP: - if (SV_RunThink(ent)) - { - 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); - } + SV_RunThink(ent); + 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); break; case MOVETYPE_STEP: SV_Physics_Step (ent); break; case MOVETYPE_WALK: - if (SV_RunThink (ent)) + SV_RunThink (ent); + // don't run physics here if running asynchronously + if (host_client->clmovement_skipphysicsframes <= 0) { if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) ) SV_AddGravity (ent); @@ -1697,31 +2120,45 @@ void SV_Physics_ClientEntity (prvm_edict_t *ent) case MOVETYPE_BOUNCEMISSILE: case MOVETYPE_FLYMISSILE: // regular thinking - if (SV_RunThink (ent)) - SV_Physics_Toss (ent); + SV_RunThink (ent); + SV_Physics_Toss (ent); break; case MOVETYPE_FLY: - if (SV_RunThink (ent)) - { - SV_CheckWater (ent); - SV_WalkMove (ent); - } + SV_RunThink (ent); + SV_CheckWater (ent); + SV_WalkMove (ent); break; default: Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype); break; } + // decrement the countdown variable used to decide when to go back to + // synchronous physics + if (host_client->clmovement_skipphysicsframes > 0) + host_client->clmovement_skipphysicsframes--; + SV_CheckVelocity (ent); - // call standard player post-think SV_LinkEdict (ent, true); SV_CheckVelocity (ent); + // call standard player post-think 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"); + + if(ent->fields.server->fixangle) + { + // angle fixing was requested by physics code... + // so store the current angles for later use + memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles)); + host_client->fixangle_angles_set = TRUE; + + // and clear fixangle for the next frame + ent->fields.server->fixangle = 0; + } } /* @@ -1754,19 +2191,8 @@ void SV_Physics (void) // 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) - { - // don't do physics on disconnected clients, FrikBot relies on this - if (!host_client->spawned) - memset(&host_client->cmd, 0, sizeof(host_client->cmd)); - // don't run physics here if running asynchronously - else if (host_client->clmovement_skipphysicsframes > 0) - host_client->clmovement_skipphysicsframes--; - else SV_Physics_ClientEntity(ent); - } - } // run physics on all the non-client entities if (!sv_freezenonclients.integer) @@ -1778,12 +2204,12 @@ void SV_Physics (void) prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1); // LordHavoc: endframe support - if (EndFrameQC) + if (prog->funcoffsets.EndFrame) { prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts); prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts); prog->globals.server->time = sv.time; - PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing"); + PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing"); } // decrement prog->num_edicts if the highest number entities died @@ -1792,51 +2218,3 @@ void SV_Physics (void) if (!sv_freezenonclients.integer) sv.time += sv.frametime; } - - -trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore) -{ - int i; - float gravity; - vec3_t move, end; - vec3_t original_origin; - vec3_t original_velocity; - vec3_t original_angles; - vec3_t original_avelocity; - prvm_eval_t *val; - trace_t trace; - - 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); - - val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity); - if (val != NULL && val->_float != 0) - gravity = val->_float; - else - gravity = 1.0; - gravity *= sv_gravity.value * 0.05; - - for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds - { - 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; - } - - 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; -} -