X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=sv_phys.c;h=71ba23eed3dfd98cb15cd1bef8d4379354d13ea8;hp=6a6dda8c68383f6e84c4159f86ab708f39ec65de;hb=0fc162a49cab157331b3e338cfb08cc2249834a0;hpb=49a0d411fae28787e20dc45a4906722a5e51b7bc diff --git a/sv_phys.c b/sv_phys.c index 6a6dda8c..71ba23ee 100644 --- a/sv_phys.c +++ b/sv_phys.c @@ -43,6 +43,25 @@ solid_edge items only clip against bsp models. void SV_Physics_Toss (prvm_edict_t *ent); +int SV_GetPitchSign(prvm_edict_t *ent) +{ + dp_model_t *model; + int modelindex; + if ( + ((modelindex = (int)ent->fields.server->modelindex) >= 1 && modelindex < MAX_MODELS && (model = sv.models[modelindex])) + ? + model->type == mod_alias + : + ( + (((unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.pflags)->_float) & PFLAGS_FULLDYNAMIC) + || + ((gamemode == GAME_TENEBRAE) && ((unsigned int)ent->fields.server->effects & (16 | 32))) + ) + ) + return -1; + return 1; +} + /* =============================================================================== @@ -77,20 +96,337 @@ int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict) return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE; } +/* +================== +SV_TracePoint +================== +*/ +trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask) +{ + int i, bodysupercontents; + int passedictprog; + float pitchsign = 1; + prvm_edict_t *traceowner, *touch; + trace_t trace; + // bounding box of entire move area + vec3_t clipboxmins, clipboxmaxs; + // size when clipping against monsters + vec3_t clipmins2, clipmaxs2; + // start and end origin of move + vec3_t clipstart; + // trace results + trace_t cliptrace; + // matrices to transform into/out of other entity's space + matrix4x4_t matrix, imatrix; + // model of other entity + dp_model_t *model; + // list of entities to test for collisions + int numtouchedicts; + prvm_edict_t *touchedicts[MAX_EDICTS]; + + //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask); + + VectorCopy(start, clipstart); + VectorClear(clipmins2); + VectorClear(clipmaxs2); +#if COLLISIONPARANOID >= 3 + Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]); +#endif + + // clip to world + Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask); + cliptrace.bmodelstartsolid = cliptrace.startsolid; + if (cliptrace.startsolid || cliptrace.fraction < 1) + cliptrace.ent = prog->edicts; + if (type == MOVE_WORLDONLY) + goto finished; + + if (type == MOVE_MISSILE) + { + // LordHavoc: modified this, was = -15, now -= 15 + for (i = 0;i < 3;i++) + { + clipmins2[i] -= 15; + clipmaxs2[i] += 15; + } + } + + // create the bounding box of the entire move + for (i = 0;i < 3;i++) + { + clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1; + clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[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); + // 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 (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]; + pitchsign = SV_GetPitchSign(touch); + } + if (model) + Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * 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 (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER) + Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask); + else + Collision_ClipPointToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask); + + Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP); + } + +finished: + return cliptrace; +} + +/* +================== +SV_TraceLine +================== +*/ +#ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND +trace_t SV_TraceLine(const vec3_t start, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask) +#else +trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask) +#endif +{ + int i, bodysupercontents; + int passedictprog; + float pitchsign = 1; + prvm_edict_t *traceowner, *touch; + trace_t trace; + // bounding box of entire move area + vec3_t clipboxmins, clipboxmaxs; + // 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 + dp_model_t *model; + // list of entities to test for collisions + int numtouchedicts; + prvm_edict_t *touchedicts[MAX_EDICTS]; +#ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND + vec3_t end; + vec_t len = 0; + + if(!VectorCompare(start, pEnd)) + { + // TRICK: make the trace 1 qu longer! + VectorSubtract(pEnd, start, end); + len = VectorNormalizeLength(end); + VectorAdd(pEnd, end, end); + } + else + VectorCopy(pEnd, end); +#endif + + //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask); + + if (VectorCompare(start, end)) + return SV_TracePoint(start, type, passedict, hitsupercontentsmask); + + VectorCopy(start, clipstart); + VectorCopy(end, clipend); + VectorClear(clipmins2); + VectorClear(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_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask); + cliptrace.bmodelstartsolid = cliptrace.startsolid; + if (cliptrace.startsolid || cliptrace.fraction < 1) + cliptrace.ent = prog->edicts; + if (type == MOVE_WORLDONLY) + goto finished; + + if (type == MOVE_MISSILE) + { + // LordHavoc: modified this, was = -15, now -= 15 + for (i = 0;i < 3;i++) + { + clipmins2[i] -= 15; + clipmaxs2[i] += 15; + } + } + + // create the bounding box of the entire move + for (i = 0;i < 3;i++) + { + clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1; + clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[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); + // 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 (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]; + pitchsign = SV_GetPitchSign(touch); + } + if (model) + Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * 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 (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER) + Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask); + else + Collision_ClipLineToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask); + + Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP); + } + +finished: +#ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND + if(!VectorCompare(start, pEnd)) + Collision_ShortenTrace(&cliptrace, len / (len + 1), pEnd); +#endif + return cliptrace; +} + /* ================== SV_Move ================== */ +#ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND #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) +trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, 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) +trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask) +#endif +#else +#if COLLISIONPARANOID >= 1 +trace_t SV_TraceBox_(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_TraceBox(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 #endif { vec3_t hullmins, hullmaxs; int i, bodysupercontents; int passedictprog; + float pitchsign = 1; qboolean pointtrace; prvm_edict_t *traceowner, *touch; trace_t trace; @@ -111,6 +447,33 @@ trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const // list of entities to test for collisions int numtouchedicts; prvm_edict_t *touchedicts[MAX_EDICTS]; +#ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND + vec3_t end; + vec_t len = 0; + + if(!VectorCompare(start, pEnd)) + { + // TRICK: make the trace 1 qu longer! + VectorSubtract(pEnd, start, end); + len = VectorNormalizeLength(end); + VectorAdd(pEnd, end, end); + } + else + VectorCopy(pEnd, end); +#endif + + if (VectorCompare(mins, maxs)) + { + vec3_t shiftstart, shiftend; + VectorAdd(start, mins, shiftstart); + VectorAdd(end, mins, shiftend); + if (VectorCompare(start, end)) + trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask); + else + trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask); + VectorSubtract(trace.endpos, mins, trace.endpos); + return trace; + } VectorCopy(start, clipstart); VectorCopy(end, clipend); @@ -128,7 +491,7 @@ trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const if (cliptrace.startsolid || cliptrace.fraction < 1) cliptrace.ent = prog->edicts; if (type == MOVE_WORLDONLY) - return cliptrace; + goto finished; if (type == MOVE_MISSILE) { @@ -218,13 +581,15 @@ trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const // 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]; + //pitchsign = 1; + pitchsign = SV_GetPitchSign(touch); } 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); + Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * 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) + if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER) Collision_ClipToGenericEntity(&trace, model, (int) 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, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask); @@ -232,20 +597,25 @@ trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP); } +finished: +#ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND + if(!VectorCompare(start, pEnd)) + Collision_ShortenTrace(&cliptrace, len / (len + 1), pEnd); +#endif 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) +trace_t SV_TraceBox(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); + trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask); if (passedict) { VectorCopy(trace.endpos, temp); - endstuck = SV_Move_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid; + endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid; #if COLLISIONPARANOID < 3 if (trace.startsolid || endstuck) #endif @@ -320,11 +690,46 @@ Linking entities into the world culling system =============================================================================== */ +void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent) +{ + 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"); +} + void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent) { int i, numtouchedicts, old_self, old_other; prvm_edict_t *touch, *touchedicts[MAX_EDICTS]; + if (ent == prog->edicts) + return; // don't add the world + + if (ent->priv.server->free) + return; + + if (ent->fields.server->solid == SOLID_NOT) + return; + // 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); @@ -342,41 +747,51 @@ void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent) 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"); + SV_LinkEdict_TouchAreaGrid_Call(touch, ent); } } prog->globals.server->self = old_self; prog->globals.server->other = old_other; } +static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs) +{ + vec3_t v, u; + matrix4x4_t m; + Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0); + + v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u); + VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs); + v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u); + if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2]; + if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2]; + v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u); + if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2]; + if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2]; + v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u); + if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2]; + if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2]; + v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u); + if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2]; + if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2]; + v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u); + if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2]; + if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2]; + v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u); + if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2]; + if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2]; + v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u); + if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2]; + if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2]; +} + /* =============== SV_LinkEdict =============== */ -void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers) +void SV_LinkEdict (prvm_edict_t *ent) { dp_model_t *model; vec3_t mins, maxs; @@ -389,10 +804,18 @@ void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers) // set the abs box - if (ent->fields.server->solid == SOLID_BSP) + if (ent->fields.server->movetype == MOVETYPE_PHYSICS) + { + // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors + // TODO special handling for spheres? + RotateBBox(ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->angles, mins, maxs); + VectorAdd(ent->fields.server->origin, mins, mins); + VectorAdd(ent->fields.server->origin, maxs, maxs); + } + else if (ent->fields.server->solid == SOLID_BSP) { int modelindex = (int)ent->fields.server->modelindex; - if (modelindex < 0 || modelindex > MAX_MODELS) + if (modelindex < 0 || modelindex >= MAX_MODELS) { Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent)); modelindex = 0; @@ -461,10 +884,6 @@ void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers) VectorCopy(maxs, ent->fields.server->absmax); World_LinkEdict(&sv.world, ent, mins, maxs); - - // if touch_triggers, call touch on all entities overlapping this box - if (touch_triggers && ent->fields.server->solid != SOLID_NOT) - SV_LinkEdict_TouchAreaGrid(ent); } /* @@ -484,11 +903,13 @@ returns true if the entity is in solid currently */ static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset) { + int contents; vec3_t org; trace_t trace; + contents = SV_GenericHitSuperContentsMask(ent); 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) + trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, contents); + if (trace.startsupercontents & contents) return true; else { @@ -512,14 +933,27 @@ static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset) 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) + if (SV_PointSuperContents(v) & contents) return true; } } } // if the trace found a better position for the entity, move it there if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001) + { +#if 0 + // please switch back to this code when trace.endpos sometimes being in solid bug is fixed VectorCopy(trace.endpos, ent->fields.server->origin); +#else + // verify if the endpos is REALLY outside solid + VectorCopy(trace.endpos, org); + trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, org, MOVE_NOMONSTERS, ent, contents); + if(trace.startsolid) + Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n"); + else + VectorCopy(org, ent->fields.server->origin); +#endif + } return false; } @@ -619,6 +1053,12 @@ void SV_CheckVelocity (prvm_edict_t *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 denormalized float + if (VectorLength2(ent->fields.server->velocity) < 0.0001) + VectorClear(ent->fields.server->velocity); + // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity); if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value) @@ -671,14 +1111,16 @@ qboolean SV_RunThink (prvm_edict_t *ent) SV_Impact Two entities have touched, so run their touch functions +returns true if the impact kept the origin of the touching entity intact ================== */ extern void VM_SetTraceGlobals(const trace_t *trace); extern sizebuf_t vm_tempstringsbuf; -void SV_Impact (prvm_edict_t *e1, trace_t *trace) +qboolean SV_Impact (prvm_edict_t *e1, trace_t *trace) { int restorevm_tempstringsbuf_cursize; int old_self, old_other; + vec3_t org; prvm_edict_t *e2 = (prvm_edict_t *)trace->ent; prvm_eval_t *val; @@ -686,6 +1128,8 @@ void SV_Impact (prvm_edict_t *e1, trace_t *trace) old_other = prog->globals.server->other; restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize; + VectorCopy(e1->fields.server->origin, org); + VM_SetTraceGlobals(trace); prog->globals.server->time = sv.time; @@ -718,6 +1162,8 @@ void SV_Impact (prvm_edict_t *e1, trace_t *trace) prog->globals.server->self = old_self; prog->globals.server->other = old_other; vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize; + + return VectorCompare(e1->fields.server->origin, org); } @@ -753,18 +1199,22 @@ Returns the clipflags if the velocity was modified (hit something solid) 1 = floor 2 = wall / step 4 = dead stop +8 = teleported by touch method If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored ============ */ static float SV_Gravity (prvm_edict_t *ent); -// LordHavoc: increased from 5 to 32 -#define MAX_CLIP_PLANES 32 +static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink); +#define MAX_CLIP_PLANES 5 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask) { int blocked, bumpcount; - int i, j, impact, numplanes; + int i, j, numplanes; float d, time_left, gravity; - vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity; + vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity; +#if 0 + vec3_t end; +#endif trace_t trace; if (time <= 0) return 0; @@ -792,8 +1242,18 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, flo if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2]) 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, hitsupercontentsmask); + VectorScale(ent->fields.server->velocity, time_left, push); +#if 0 + VectorAdd(ent->fields.server->origin, push, end); +#endif + if(!SV_PushEntity(&trace, ent, push, false, false)) + { + // we got teleported by a touch function + // let's abort the move + blocked |= 8; + break; + } + #if 0 //if (trace.fraction < 0.002) { @@ -804,7 +1264,7 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, flo 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, hitsupercontentsmask); + testtrace = SV_TraceBox(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); @@ -819,7 +1279,7 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, flo 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, hitsupercontentsmask); + testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask); if (trace.fraction < testtrace.fraction) { trace = testtrace; @@ -852,27 +1312,21 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, flo } #endif - // break if it moved the entire distance if (trace.fraction == 1) - { - VectorCopy(trace.endpos, ent->fields.server->origin); break; - } - - if (!trace.ent) - { - Con_Printf ("SV_FlyMove: !trace.ent"); - trace.ent = prog->edicts; - } - - impact = !((int) ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent); - if (trace.plane.normal[2]) { if (trace.plane.normal[2] > 0.7) { // floor blocked |= 1; + + if (!trace.ent) + { + Con_Printf ("SV_FlyMove: !trace.ent"); + trace.ent = prog->edicts; + } + ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND; ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent); } @@ -885,25 +1339,13 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, flo if (stepnormal) VectorCopy(trace.plane.normal, stepnormal); } - if (trace.fraction >= 0.001) { // actually covered some distance - VectorCopy(trace.endpos, ent->fields.server->origin); VectorCopy(ent->fields.server->velocity, original_velocity); numplanes = 0; } - // run the impact function - if (impact) - { - SV_Impact(ent, &trace); - - // break if removed by the impact function - if (ent->priv.server->free) - break; - } - time_left *= 1 - trace.fraction; // clipped to another plane @@ -988,12 +1430,12 @@ static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, flo { // 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, hitsupercontentsmask); + trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask); } */ // LordHavoc: this came from QW and allows you to get out of water more easily - if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP)) + if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8)) VectorCopy(primal_velocity, ent->fields.server->velocity); if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND)) ent->fields.server->velocity[2] -= gravity; @@ -1033,14 +1475,18 @@ PUSHMOVE SV_PushEntity Does not change the entities velocity at all +The trace struct is filled with the trace that has been done. +Returns true if the push did not result in the entity being teleported by QC code. ============ */ -static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid) +static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink) { int type; - trace_t trace; + int bump; + vec3_t original; vec3_t end; + VectorCopy(ent->fields.server->origin, original); VectorAdd (ent->fields.server->origin, push, end); if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE) @@ -1050,16 +1496,41 @@ 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, SV_GenericHitSuperContentsMask(ent)); - if (trace.bmodelstartsolid && failonbmodelstartsolid) - return trace; + *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent)); + bump = 0; + while (trace->startsolid && sv_gameplayfix_nudgeoutofsolid.integer) + { + VectorMA(ent->fields.server->origin, -trace->startdepth, trace->startdepthnormal, ent->fields.server->origin); + *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent)); + bump++; + if (bump > 10) + { + VectorCopy(original, ent->fields.server->origin); + break; + } + } + if (trace->bmodelstartsolid && failonbmodelstartsolid) + return true; - VectorCopy (trace.endpos, ent->fields.server->origin); - SV_LinkEdict (ent, true); - if (ent->fields.server->solid >= SOLID_TRIGGER && trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent))) - SV_Impact (ent, &trace); - return trace; + VectorCopy (trace->endpos, ent->fields.server->origin); + SV_LinkEdict(ent); + +#if 0 + if(!trace->startsolid) + if(SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, type, ent, SV_GenericHitSuperContentsMask(ent)).startsolid) + { + Con_Printf("something eeeeevil happened\n"); + } +#endif + + if (dolink) + SV_LinkEdict_TouchAreaGrid(ent); + + if((ent->fields.server->solid >= SOLID_TRIGGER && trace->ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace->ent)))) + return SV_Impact (ent, trace); + + return true; } @@ -1081,7 +1552,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) int numcheckentities; static prvm_edict_t *checkentities[MAX_EDICTS]; dp_model_t *pushermodel; - trace_t trace; + trace_t trace, trace2; matrix4x4_t pusherfinalmatrix, pusherfinalimatrix; unsigned short moved_edicts[MAX_EDICTS]; @@ -1108,7 +1579,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0)); pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0)); pusher->fields.server->ltime += movetime; - SV_LinkEdict (pusher, false); + SV_LinkEdict(pusher); return; default: Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid); @@ -1190,7 +1661,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) 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); pusher->fields.server->ltime += movetime; - SV_LinkEdict (pusher, false); + SV_LinkEdict(pusher); pushermodel = NULL; if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS) @@ -1207,12 +1678,18 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) for (e = 0;e < numcheckentities;e++) { 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) + int movetype = (int)check->fields.server->movetype; + switch(movetype) + { + case MOVETYPE_NONE: + case MOVETYPE_PUSH: + case MOVETYPE_FOLLOW: + case MOVETYPE_NOCLIP: + case MOVETYPE_FAKEPUSH: continue; + default: + break; + } if (check->fields.server->owner == pusherprog) continue; @@ -1233,7 +1710,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher) { Collision_ClipToGenericEntity(&trace, pushermodel, (int) 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); - //trace = SV_Move(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents); + //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents); if (!trace.startsolid) { //Con_Printf("- not in solid\n"); @@ -1260,9 +1737,24 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles); moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check); + // physics objects need better collisions than this code can do + if (movetype == MOVETYPE_PHYSICS) + { + VectorAdd(check->fields.server->origin, move, check->fields.server->origin); + SV_LinkEdict(check); + SV_LinkEdict_TouchAreaGrid(check); + continue; + } + // try moving the contacted entity pusher->fields.server->solid = SOLID_NOT; - trace = SV_PushEntity (check, move, true); + if(!SV_PushEntity (&trace, check, move, true, true)) + { + // entity "check" got teleported + check->fields.server->angles[1] += trace.fraction * moveangle[1]; + pusher->fields.server->solid = savesolid; // was SOLID_BSP + continue; // pushed enough + } // FIXME: turn players specially check->fields.server->angles[1] += trace.fraction * moveangle[1]; pusher->fields.server->solid = savesolid; // was SOLID_BSP @@ -1284,7 +1776,11 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) 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); + if(!SV_PushEntity (&trace2, check, move2, true, true)) + { + // entity "check" got teleported + continue; + } pusher->fields.server->solid = savesolid; Collision_ClipToGenericEntity(&trace, pushermodel, (int) 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) @@ -1294,7 +1790,11 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) 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); + if(!SV_PushEntity (&trace2, check, move2, true, true)) + { + // entity "check" got teleported + continue; + } pusher->fields.server->solid = savesolid; Collision_ClipToGenericEntity(&trace, pushermodel, (int) 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) @@ -1315,7 +1815,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) VectorCopy (pushorig, pusher->fields.server->origin); VectorCopy (pushang, pusher->fields.server->angles); pusher->fields.server->ltime = pushltime; - SV_LinkEdict (pusher, false); + SV_LinkEdict(pusher); // move back any entities we already moved for (i = 0;i < num_moved;i++) @@ -1323,7 +1823,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime) 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); + SV_LinkEdict(ed); } // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone @@ -1390,111 +1890,127 @@ CLIENT MOVEMENT static float unstickoffsets[] = { + // poutting -/+z changes first as they are least weird + 0, 0, -1, + 0, 0, 1, + // x or y changes -1, 0, 0, 1, 0, 0, 0, -1, 0, 0, 1, 0, + // x and y changes -1, -1, 0, 1, -1, 0, -1, 1, 0, 1, 1, 0, - 0, 0, -1, - 0, 0, 1, - 0, 0, -2, - 0, 0, 2, - 0, 0, -3, - 0, 0, 3, - 0, 0, -4, - 0, 0, 4, - 0, 0, -5, - 0, 0, 5, - 0, 0, -6, - 0, 0, 6, - 0, 0, -7, - 0, 0, 7, - 0, 0, -8, - 0, 0, 8, - 0, 0, -9, - 0, 0, 9, - 0, 0, -10, - 0, 0, 10, - 0, 0, -11, - 0, 0, 11, - 0, 0, -12, - 0, 0, 12, - 0, 0, -13, - 0, 0, 13, - 0, 0, -14, - 0, 0, 14, - 0, 0, -15, - 0, 0, 15, - 0, 0, -16, - 0, 0, 16, - 0, 0, -17, - 0, 0, 17, }; -/* -============= -SV_CheckStuck +typedef enum unstickresult_e +{ + UNSTICK_STUCK = 0, + UNSTICK_GOOD = 1, + UNSTICK_UNSTUCK = 2 +} +unstickresult_t; -This is a big hack to try and fix the rare case of getting stuck in the world -clipping hull. -============= -*/ -void SV_CheckStuck (prvm_edict_t *ent) +unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset) { - int i; - vec3_t offset; + int i, maxunstick; + // if not stuck in a bmodel, just return if (!SV_TestEntityPosition(ent, vec3_origin)) - { - VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin); - return; - } + return UNSTICK_GOOD; 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; + VectorCopy(unstickoffsets + i, offset); + SV_LinkEdict(ent); + //SV_LinkEdict_TouchAreaGrid(ent); + return UNSTICK_UNSTUCK; } } - VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset); - if (!SV_TestEntityPosition(ent, offset)) + maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36); + // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox + + for(i = 2; i <= maxunstick; ++i) { - 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; + VectorClear(offset); + offset[2] = -i; + if (!SV_TestEntityPosition(ent, offset)) + { + SV_LinkEdict(ent); + //SV_LinkEdict_TouchAreaGrid(ent); + return UNSTICK_UNSTUCK; + } + offset[2] = i; + if (!SV_TestEntityPosition(ent, offset)) + { + SV_LinkEdict(ent); + //SV_LinkEdict_TouchAreaGrid(ent); + return UNSTICK_UNSTUCK; + } } - Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname)); + return UNSTICK_STUCK; } qboolean SV_UnstickEntity (prvm_edict_t *ent) { - int i; - - // if not stuck in a bmodel, just return - if (!SV_TestEntityPosition(ent, vec3_origin)) - return true; - - for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3) + vec3_t offset; + switch(SV_UnstickEntityReturnOffset(ent, offset)) { - 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); + case UNSTICK_GOOD: return true; - } + case UNSTICK_UNSTUCK: + 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), offset[0], offset[1], offset[2]); + return true; + case UNSTICK_STUCK: + if (developer.integer >= 100) + Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname)); + return false; + default: + Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n"); + return false; } +} - if (developer.integer >= 100) - Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname)); - return false; +/* +============= +SV_CheckStuck + +This is a big hack to try and fix the rare case of getting stuck in the world +clipping hull. +============= +*/ +void SV_CheckStuck (prvm_edict_t *ent) +{ + vec3_t offset; + + switch(SV_UnstickEntityReturnOffset(ent, offset)) + { + case UNSTICK_GOOD: + VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin); + break; + case UNSTICK_UNSTUCK: + 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), offset[0], offset[1], offset[2]); + break; + case UNSTICK_STUCK: + 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); + //SV_LinkEdict_TouchAreaGrid(ent); + } + else + Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname)); + break; + default: + Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n"); + } } @@ -1607,7 +2123,7 @@ int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel) case 7: dir[0] = -2; dir[1] = -2; break; } - SV_PushEntity (ent, dir, false); + SV_PushEntity (&trace, ent, dir, false, true); // retry the original move ent->fields.server->velocity[0] = oldvel[0]; @@ -1644,7 +2160,7 @@ void SV_WalkMove (prvm_edict_t *ent) { 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; + trace_t downtrace, trace; qboolean applygravity; // if frametime is 0 (due to client sending the same timestamp twice), @@ -1673,13 +2189,11 @@ void SV_WalkMove (prvm_edict_t *ent) ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND; SV_CheckVelocity(ent); - SV_LinkEdict (ent, true); + SV_LinkEdict(ent); + SV_LinkEdict_TouchAreaGrid(ent); - VectorCopy(ent->fields.server->origin, originalmove_origin); - VectorCopy(ent->fields.server->velocity, originalmove_velocity); - originalmove_clip = clip; - originalmove_flags = (int)ent->fields.server->flags; - originalmove_groundentity = ent->fields.server->groundentity; + if(clip & 8) // teleport + return; if ((int)ent->fields.server->flags & FL_WATERJUMP) return; @@ -1687,6 +2201,12 @@ void SV_WalkMove (prvm_edict_t *ent) if (sv_nostep.integer) return; + VectorCopy(ent->fields.server->origin, originalmove_origin); + VectorCopy(ent->fields.server->velocity, originalmove_velocity); + originalmove_clip = clip; + originalmove_flags = (int)ent->fields.server->flags; + originalmove_groundentity = ent->fields.server->groundentity; + // if move didn't block on a step, return if (clip & 2) { @@ -1714,16 +2234,26 @@ void SV_WalkMove (prvm_edict_t *ent) // move up VectorClear (upmove); upmove[2] = sv_stepheight.value; - // FIXME: don't link? - SV_PushEntity(ent, upmove, false); + if(!SV_PushEntity(&trace, ent, upmove, false, true)) + { + // we got teleported when upstepping... must abort the move + return; + } // move forward ent->fields.server->velocity[2] = 0; clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask); ent->fields.server->velocity[2] += start_velocity[2]; + if(clip & 8) + { + // we got teleported when upstepping... must abort the move + // note that z velocity handling may not be what QC expects here, but we cannot help it + return; + } SV_CheckVelocity(ent); - SV_LinkEdict (ent, true); + SV_LinkEdict(ent); + SV_LinkEdict_TouchAreaGrid(ent); // check for stuckness, possibly due to the limited precision of floats // in the clipping hulls @@ -1756,8 +2286,11 @@ void SV_WalkMove (prvm_edict_t *ent) // move down VectorClear (downmove); downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime; - // FIXME: don't link? - downtrace = SV_PushEntity (ent, downmove, false); + if(!SV_PushEntity (&downtrace, ent, downmove, false, true)) + { + // we got teleported when downstepping... must abort the move + return; + } if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7) { @@ -1787,7 +2320,8 @@ void SV_WalkMove (prvm_edict_t *ent) } SV_CheckVelocity(ent); - SV_LinkEdict (ent, true); + SV_LinkEdict(ent); + SV_LinkEdict_TouchAreaGrid(ent); } //============================================================================ @@ -1833,7 +2367,8 @@ void SV_Physics_Follow (prvm_edict_t *ent) ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2]; } VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles); - SV_LinkEdict (ent, true); + SV_LinkEdict(ent); + //SV_LinkEdict_TouchAreaGrid(ent); } /* @@ -1918,7 +2453,8 @@ void SV_Physics_Toss (prvm_edict_t *ent) // and groundentity is now freed, set groundentity to 0 (world) // which leaves it suspended in the air ent->fields.server->groundentity = 0; - return; + if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer) + return; } } ent->priv.server->suspendedinairflag = false; @@ -1937,14 +2473,16 @@ void SV_Physics_Toss (prvm_edict_t *ent) { // move origin VectorScale (ent->fields.server->velocity, movetime, move); - trace = SV_PushEntity (ent, move, true); + if(!SV_PushEntity (&trace, ent, move, true, true)) + return; // teleported if (ent->priv.server->free) return; if (trace.bmodelstartsolid) { // try to unstick the entity SV_UnstickEntity(ent); - trace = SV_PushEntity (ent, move, false); + if(!SV_PushEntity (&trace, ent, move, false, true)) + return; // teleported if (ent->priv.server->free) return; } @@ -1953,14 +2491,31 @@ void SV_Physics_Toss (prvm_edict_t *ent) movetime *= 1 - min(1, trace.fraction); if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) { - ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0); + prvm_eval_t *val; + float bouncefactor = 1.0f; + val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor); + if (val!=0 && val->_float) + bouncefactor = val->_float; + + ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor); ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND; } else if (ent->fields.server->movetype == MOVETYPE_BOUNCE) { float d, ent_gravity; prvm_eval_t *val; - ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5); + float bouncefactor = 0.5f; + float bouncestop = 60.0f / 800.0f; + + val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor); + if (val!=0 && val->_float) + bouncefactor = val->_float; + + val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop); + if (val!=0 && val->_float) + bouncestop = val->_float; + + ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor); // LordHavoc: fixed grenades not bouncing when fired down a slope val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity); if (val!=0 && val->_float) @@ -1970,7 +2525,7 @@ void SV_Physics_Toss (prvm_edict_t *ent) if (sv_gameplayfix_grenadebouncedownslopes.integer) { d = DotProduct(trace.plane.normal, ent->fields.server->velocity); - if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * (60.0 / 800.0) * ent_gravity) + if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity) { ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND; ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent); @@ -1982,7 +2537,7 @@ void SV_Physics_Toss (prvm_edict_t *ent) } else { - if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * (60.0 / 800.0) * ent_gravity) + if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity) { ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND; ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent); @@ -2057,7 +2612,8 @@ void SV_Physics_Step (prvm_edict_t *ent) ent->fields.server->flags -= FL_ONGROUND; SV_CheckVelocity(ent); SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent)); - SV_LinkEdict(ent, true); + SV_LinkEdict(ent); + SV_LinkEdict_TouchAreaGrid(ent); ent->priv.server->waterposition_forceupdate = true; } } @@ -2068,7 +2624,8 @@ void SV_Physics_Step (prvm_edict_t *ent) SV_CheckVelocity(ent); SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent)); - SV_LinkEdict(ent, true); + SV_LinkEdict(ent); + SV_LinkEdict_TouchAreaGrid(ent); // just hit ground if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND) @@ -2121,7 +2678,7 @@ static void SV_Physics_Entity (prvm_edict_t *ent) // ents in the first frame regardless) qboolean runmove = ent->priv.server->move; ent->priv.server->move = true; - if (!runmove && sv_gameplayfix_delayprojectiles.integer) + if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0) return; switch ((int) ent->fields.server->movetype) { @@ -2144,7 +2701,7 @@ static void SV_Physics_Entity (prvm_edict_t *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_LinkEdict(ent, false); + SV_LinkEdict(ent); break; case MOVETYPE_STEP: SV_Physics_Step (ent); @@ -2162,6 +2719,13 @@ static void SV_Physics_Entity (prvm_edict_t *ent) if (SV_RunThink (ent)) SV_Physics_Toss (ent); break; + case MOVETYPE_PHYSICS: + if (SV_RunThink(ent)) + { + SV_LinkEdict(ent); + SV_LinkEdict_TouchAreaGrid(ent); + } + break; default: Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype); break; @@ -2186,12 +2750,6 @@ void SV_Physics_ClientMove(void) // 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); // perform MOVETYPE_WALK behavior SV_WalkMove (ent); @@ -2215,14 +2773,14 @@ void SV_Physics_ClientMove(void) } } -void SV_Physics_ClientEntity(prvm_edict_t *ent) +static void SV_Physics_ClientEntity_PreThink(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; - } + + // make sure the velocity is sane (not a NaN) + SV_CheckVelocity(ent); // don't run physics here if running asynchronously if (host_client->clmovement_inputtimeout <= 0) @@ -2231,20 +2789,65 @@ void SV_Physics_ClientEntity(prvm_edict_t *ent) //host_client->cmd.time = max(host_client->cmd.time, sv.time); } - // make sure the velocity is sane (not a NaN) + // make sure the velocity is still 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); + PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing"); + + // make sure the velocity is still sane (not a NaN) + SV_CheckVelocity(ent); +} + +static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent) +{ + // don't do physics on disconnected clients, FrikBot relies on this + if (!host_client->spawned) + return; + + // make sure the velocity is sane (not a NaN) + 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"); + + // make sure the velocity is still sane (not a NaN) + SV_CheckVelocity(ent); + + 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; + } + + // decrement the countdown variable used to decide when to go back to + // synchronous physics + if (host_client->clmovement_inputtimeout > sv.frametime) + host_client->clmovement_inputtimeout -= sv.frametime; + else + host_client->clmovement_inputtimeout = 0; +} + +static 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; + } + + // make sure the velocity is sane (not a NaN) + SV_CheckVelocity(ent); switch ((int) ent->fields.server->movetype) { @@ -2287,39 +2890,20 @@ void SV_Physics_ClientEntity(prvm_edict_t *ent) SV_RunThink (ent); SV_WalkMove (ent); break; + case MOVETYPE_PHYSICS: + SV_RunThink (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_inputtimeout > sv.frametime) - host_client->clmovement_inputtimeout -= sv.frametime; - else - host_client->clmovement_inputtimeout = 0; - SV_CheckVelocity (ent); - SV_LinkEdict (ent, true); + SV_LinkEdict(ent); + SV_LinkEdict_TouchAreaGrid(ent); 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; - } } /* @@ -2340,6 +2924,9 @@ void SV_Physics (void) prog->globals.server->frametime = sv.frametime; PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing"); + // run physics engine + World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value); + // // treat each object in turn // @@ -2348,18 +2935,50 @@ void SV_Physics (void) 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 + SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary + + if (sv_gameplayfix_consistentplayerprethink.integer) + { + // run physics on the client entities in 3 stages + 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) + SV_Physics_ClientEntity_PreThink(ent); + + 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) + SV_Physics_ClientEntity(ent); - // 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) + 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) + SV_Physics_ClientEntity_PostThink(ent); + } + else + { + // 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) + { + SV_Physics_ClientEntity_PreThink(ent); SV_Physics_ClientEntity(ent); + SV_Physics_ClientEntity_PostThink(ent); + } + } + } // 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); + // make a second pass to see if any ents spawned this frame and make + // sure they run their move/think + if (sv_gameplayfix_delayprojectiles.integer < 0) + for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent)) + if (!ent->priv.server->move && !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); @@ -2374,7 +2993,7 @@ void SV_Physics (void) } // decrement prog->num_edicts if the highest number entities died - for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--); + for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--); if (!sv_freezenonclients.integer) sv.time += sv.frametime;