X-Git-Url: https://de.git.xonotic.org/?a=blobdiff_plain;f=sv_phys.c;h=1e58ea8b8b0b97b76d8080cca7f9d8c8ac09a149;hb=d3e2f7e92ec8883147b70a7bc0971a7b4561d3d5;hp=60e36273ed618c56fc78f3bc018cdfe7ba614ed2;hpb=96b2ceae75ede99972a8c0d33528e9ced000c645;p=xonotic%2Fdarkplaces.git diff --git a/sv_phys.c b/sv_phys.c index 60e36273..1e58ea8b 100644 --- a/sv_phys.c +++ b/sv_phys.c @@ -39,17 +39,20 @@ solid_edge items only clip against bsp models. */ -cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4"}; -cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100"}; -cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800"}; -cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000"}; -cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0"}; -cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18"}; -cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "1"}; -cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1"}; -cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0"}; -cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0"}; -cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1"}; +cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4", "how fast you slow down"}; +cvar_t sv_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", "1", "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)"}; #define MOVE_EPSILON 0.01 @@ -64,6 +67,9 @@ void SV_Phys_Init (void) Cvar_RegisterVariable(&sv_freezenonclients); Cvar_RegisterVariable(&sv_playerphysicsqc); + + Cvar_RegisterVariable(&sv_sound_watersplash); + Cvar_RegisterVariable(&sv_sound_land); } /* @@ -359,7 +365,7 @@ int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal) // run the impact function if (impact) { - SV_Impact(ent, trace.ent); + SV_Impact(ent, (prvm_edict_t *)trace.ent); // break if removed by the impact function if (ent->priv.server->free) @@ -512,7 +518,7 @@ trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push) SV_LinkEdict (ent, true); if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent))) - SV_Impact (ent, trace.ent); + SV_Impact (ent, (prvm_edict_t *)trace.ent); return trace; } @@ -523,7 +529,6 @@ SV_PushMove ============ */ -trace_t SV_ClipMoveToEntity (prvm_edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int movetype, int hitsupercontents); void SV_PushMove (prvm_edict_t *pusher, float movetime) { int i, e, index; @@ -824,7 +829,7 @@ void SV_CheckStuck (prvm_edict_t *ent) VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin); if (!SV_TestEntityPosition(ent)) { - Con_DPrint("Unstuck.\n"); + Con_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname)); SV_LinkEdict (ent, true); return; } @@ -838,14 +843,40 @@ void SV_CheckStuck (prvm_edict_t *ent) ent->fields.server->origin[2] = org[2] + z; if (!SV_TestEntityPosition(ent)) { - Con_DPrint("Unstuck.\n"); + Con_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname)); + SV_LinkEdict (ent, true); + return; + } + } + + VectorCopy (org, ent->fields.server->origin); + Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname)); +} + +void SV_UnstickEntity (prvm_edict_t *ent) +{ + int i, j, z; + vec3_t org; + + VectorCopy (ent->fields.server->origin, org); + + for (z=0 ; 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\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname)); SV_LinkEdict (ent, true); return; } } VectorCopy (org, ent->fields.server->origin); - Con_DPrint("player is stuck.\n"); + Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname)); } @@ -1164,7 +1195,7 @@ SV_CheckWaterTransition void SV_CheckWaterTransition (prvm_edict_t *ent) { int cont; - cont = SV_PointQ1Contents(ent->fields.server->origin); + cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin)); if (!ent->fields.server->watertype) { // just spawned here @@ -1174,8 +1205,8 @@ void SV_CheckWaterTransition (prvm_edict_t *ent) } // check if the entity crossed into or out of water - if (gamemode != GAME_NEXUIZ && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME))) - SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1); + if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME))) + SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1); if (cont <= CONTENTS_WATER) { @@ -1201,22 +1232,25 @@ void SV_Physics_Toss (prvm_edict_t *ent) trace_t trace; vec3_t move; - // don't stick to ground if onground and moving upward - if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND)) - ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND; - // if onground, return without moving if ((int)ent->fields.server->flags & FL_ONGROUND) { - if (ent->fields.server->groundentity == 0 || sv_gameplayfix_noairborncorpse.integer) - return; - // if ent was supported by a brush model on previous frame, - // and groundentity is now freed, set groundentity to 0 (floating) - if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free) + // don't stick to ground if onground and moving upward + if (ent->fields.server->velocity[2] >= (1.0 / 32.0)) + ent->fields.server->flags -= FL_ONGROUND; + else { - // leave it suspended in the air - ent->fields.server->groundentity = 0; - return; + prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity); + if (ground->fields.server->solid == SOLID_BSP || !sv_gameplayfix_noairborncorpse.integer) + return; + // if ent was supported by a brush model on previous frame, + // and groundentity is now freed, set groundentity to 0 (floating) + if (ent->priv.server->suspendedinairflag && ground->priv.server->free) + { + // leave it suspended in the air + ent->fields.server->groundentity = 0; + return; + } } } ent->priv.server->suspendedinairflag = false; @@ -1235,6 +1269,14 @@ void SV_Physics_Toss (prvm_edict_t *ent) trace = SV_PushEntity (ent, move); if (ent->priv.server->free) return; + if (trace.startsolid) + { + // try to unstick the entity + SV_UnstickEntity(ent); + trace = SV_PushEntity (ent, move); + if (ent->priv.server->free) + return; + } if (trace.fraction < 1) { @@ -1316,23 +1358,38 @@ will fall if the floor is pulled out from under them. */ void SV_Physics_Step (prvm_edict_t *ent) { - // don't stick to ground if onground and moving upward - if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND)) - ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND; - - // freefall if not onground/fly/swim - if (!((int)ent->fields.server->flags & (FL_ONGROUND | FL_FLY | FL_SWIM))) + int flags = (int)ent->fields.server->flags; + // don't fall at all if fly/swim + if (!(flags & (FL_FLY | FL_SWIM))) { - int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1; + if (flags & FL_ONGROUND) + { + // freefall if onground and moving upward + // freefall if not standing on a world surface (it may be a lift) + prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity); + if (ent->fields.server->velocity[2] >= (1.0 / 32.0) || (ground->fields.server->solid != SOLID_BSP && sv_gameplayfix_noairborncorpse.integer)) + { + ent->fields.server->flags -= FL_ONGROUND; + SV_AddGravity(ent); + SV_CheckVelocity(ent); + SV_FlyMove(ent, sv.frametime, NULL); + SV_LinkEdict(ent, true); + } + } + else + { + // freefall if not onground + int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1; - SV_AddGravity(ent); - SV_CheckVelocity(ent); - SV_FlyMove(ent, sv.frametime, NULL); - SV_LinkEdict(ent, true); + SV_AddGravity(ent); + SV_CheckVelocity(ent); + SV_FlyMove(ent, sv.frametime, NULL); + SV_LinkEdict(ent, true); - // just hit ground - if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && gamemode != GAME_NEXUIZ) - SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1); + // just hit ground + if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string) + SV_StartSound(ent, 0, sv_sound_land.string, 255, 1); + } } // regular thinking @@ -1343,38 +1400,92 @@ void SV_Physics_Step (prvm_edict_t *ent) //============================================================================ -void SV_Physics_Entity (prvm_edict_t *ent, qboolean runmove) +static void SV_Physics_Entity (prvm_edict_t *ent) { - int i = ent - prog->edicts; - if (i >= 1 && i <= svs.maxclients) + // don't run a move on newly spawned projectiles as it messes up movement + // interpolation and rocket trails + qboolean runmove = ent->priv.server->move; + ent->priv.server->move = true; + switch ((int) ent->fields.server->movetype) { - // make sure the velocity is sane (not a NaN) - SV_CheckVelocity(ent); - // LordHavoc: QuakeC replacement for SV_ClientThink (player movement) - if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer) + case MOVETYPE_PUSH: + case MOVETYPE_FAKEPUSH: + SV_Physics_Pusher (ent); + break; + case MOVETYPE_NONE: + // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects + if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime) + SV_RunThink (ent); + break; + case MOVETYPE_FOLLOW: + SV_Physics_Follow (ent); + break; + case MOVETYPE_NOCLIP: + if (SV_RunThink(ent)) { - prog->globals.server->time = sv.time; - prog->globals.server->self = PRVM_EDICT_TO_PROG(ent); - PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing"); + SV_CheckWater(ent); + VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin); + VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles); } - else - SV_ClientThink (); - // make sure the velocity is sane (not a NaN) - SV_CheckVelocity(ent); - // LordHavoc: a hack to ensure that the (rather silly) id1 quakec - // player_run/player_stand1 does not horribly malfunction if the - // velocity becomes a number that is both == 0 and != 0 - // (sounds to me like NaN but to be absolutely safe...) - if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001) - VectorClear(ent->fields.server->velocity); - // call standard client pre-think + SV_LinkEdict(ent, false); + break; + case MOVETYPE_STEP: + SV_Physics_Step (ent); + break; + case MOVETYPE_WALK: + if (SV_RunThink (ent)) + { + if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) ) + SV_AddGravity (ent); + SV_CheckStuck (ent); + SV_WalkMove (ent); + SV_LinkEdict (ent, true); + } + break; + case MOVETYPE_TOSS: + case MOVETYPE_BOUNCE: + case MOVETYPE_BOUNCEMISSILE: + case MOVETYPE_FLYMISSILE: + case MOVETYPE_FLY: + // regular thinking + if (SV_RunThink (ent) && runmove) + SV_Physics_Toss (ent); + break; + default: + Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype); + break; + } +} + +void SV_ApplyClientMove (void); +void SV_Physics_ClientEntity (prvm_edict_t *ent) +{ + SV_ApplyClientMove(); + // make sure the velocity is sane (not a NaN) + SV_CheckVelocity(ent); + // LordHavoc: QuakeC replacement for SV_ClientThink (player movement) + if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer) + { prog->globals.server->time = sv.time; prog->globals.server->self = PRVM_EDICT_TO_PROG(ent); - PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing"); - SV_CheckVelocity (ent); + PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing"); } + else + SV_ClientThink (); + // make sure the velocity is sane (not a NaN) + SV_CheckVelocity(ent); + // LordHavoc: a hack to ensure that the (rather silly) id1 quakec + // player_run/player_stand1 does not horribly malfunction if the + // velocity becomes a number that is both == 0 and != 0 + // (sounds to me like NaN but to be absolutely safe...) + if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001) + VectorClear(ent->fields.server->velocity); + // call standard client pre-think + prog->globals.server->time = sv.time; + prog->globals.server->self = PRVM_EDICT_TO_PROG(ent); + PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing"); + SV_CheckVelocity (ent); - // LordHavoc: merged client and normal entity physics switch ((int) ent->fields.server->movetype) { case MOVETYPE_PUSH: @@ -1396,9 +1507,6 @@ void SV_Physics_Entity (prvm_edict_t *ent, qboolean runmove) VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin); VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles); } - // relink normal entities here, players always get relinked so don't relink twice - if (!(i > 0 && i <= svs.maxclients)) - SV_LinkEdict(ent, false); break; case MOVETYPE_STEP: SV_Physics_Step (ent); @@ -1410,9 +1518,6 @@ void SV_Physics_Entity (prvm_edict_t *ent, qboolean runmove) SV_AddGravity (ent); SV_CheckStuck (ent); SV_WalkMove (ent); - // relink normal entities here, players always get relinked so don't relink twice - if (!(i > 0 && i <= svs.maxclients)) - SV_LinkEdict (ent, true); } break; case MOVETYPE_TOSS: @@ -1420,42 +1525,33 @@ void SV_Physics_Entity (prvm_edict_t *ent, qboolean runmove) case MOVETYPE_BOUNCEMISSILE: case MOVETYPE_FLYMISSILE: // regular thinking - if (SV_RunThink (ent) && runmove) + if (SV_RunThink (ent)) SV_Physics_Toss (ent); break; case MOVETYPE_FLY: - if (SV_RunThink (ent) && runmove) + if (SV_RunThink (ent)) { - if (i > 0 && i <= svs.maxclients) - { - SV_CheckWater (ent); - SV_WalkMove (ent); - } - else - SV_Physics_Toss (ent); + SV_CheckWater (ent); + SV_WalkMove (ent); } break; default: - Con_Printf ("SV_Physics: bad movetype %i", (int)ent->fields.server->movetype); + Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype); break; } - if (i >= 1 && i <= svs.maxclients) - { - SV_CheckVelocity (ent); + SV_CheckVelocity (ent); - // call standard player post-think - SV_LinkEdict (ent, true); + // call standard player post-think + SV_LinkEdict (ent, true); - SV_CheckVelocity (ent); + SV_CheckVelocity (ent); - prog->globals.server->time = sv.time; - prog->globals.server->self = PRVM_EDICT_TO_PROG(ent); - PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing"); - } + prog->globals.server->time = sv.time; + prog->globals.server->self = PRVM_EDICT_TO_PROG(ent); + PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing"); } - /* ================ SV_Physics @@ -1464,9 +1560,8 @@ SV_Physics */ void SV_Physics (void) { - int i, newnum_edicts; + int i; prvm_edict_t *ent; - qbyte runmove[MAX_EDICTS]; // let the progs know that a new frame has started prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts); @@ -1475,43 +1570,36 @@ void SV_Physics (void) prog->globals.server->frametime = sv.frametime; PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing"); - newnum_edicts = 0; - for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent)) - if ((runmove[i] = !ent->priv.server->free)) - newnum_edicts = i + 1; - prog->num_edicts = max(svs.maxclients + 1, newnum_edicts); - // // treat each object in turn // - for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent)) - { - if (ent->priv.server->free) - continue; - - if (prog->globals.server->force_retouch) - SV_LinkEdict (ent, true); // force retouch even for stationary + // if force_retouch, relink all the entities + if (prog->globals.server->force_retouch > 0) + for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent)) + if (!ent->priv.server->free) + SV_LinkEdict (ent, true); // force retouch even for stationary - if (i >= 1 && i <= svs.maxclients) + // run physics on the client entities + for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++) + { + if (!ent->priv.server->free) { - host_client = svs.clients + i - 1; // don't do physics on disconnected clients, FrikBot relies on this if (!host_client->spawned) - { memset(&host_client->cmd, 0, sizeof(host_client->cmd)); - continue; - } - // connected slot - if (host_client->movesequence) - continue; // return if running asynchronously + // don't run physics here if running asynchronously + else if (!host_client->movesequence) + SV_Physics_ClientEntity(ent); } - else if (sv_freezenonclients.integer) - continue; - - SV_Physics_Entity(ent, runmove[i]); } + // run physics on all the non-client entities + if (!sv_freezenonclients.integer) + for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent)) + if (!ent->priv.server->free) + SV_Physics_Entity(ent); + if (prog->globals.server->force_retouch > 0) prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1); @@ -1524,6 +1612,9 @@ void SV_Physics (void) PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing"); } + // decrement prog->num_edicts if the highest number entities died + for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--); + if (!sv_freezenonclients.integer) sv.time += sv.frametime; }