/* ============= SV_PhysicsNoclip A moving object that doesn't obey physics ============= */ void SV_Physics_Noclip( edict_t *ent ) { // regular thinking if( !SV_RunThink( ent )) return; SV_CheckWater( ent ); VectorMA( ent->v.origin, host.frametime, ent->v.velocity, ent->v.origin ); VectorMA( ent->v.angles, host.frametime, ent->v.avelocity, ent->v.angles ); // noclip ents never touch triggers SV_LinkEdict( ent, false ); }
void SV_Physics_Step (edict_t *ent) { qboolean wasonground; qboolean inwater; qboolean hitsound = false; float *vel; float speed, newspeed, control; float friction; edict_t *groundentity; groundentity = PROG_TO_EDICT(ent->v.groundentity); if ((int)groundentity->v.flags & FL_CONVEYOR) VectorScale(groundentity->v.movedir, groundentity->v.speed, ent->v.basevelocity); else VectorCopy(vec_origin, ent->v.basevelocity); //@@ pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(ent); PF_WaterMove(); SV_CheckVelocity (ent); wasonground = (int)ent->v.flags & FL_ONGROUND; // ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; // add gravity except: // flying monsters // swimming monsters who are in the water inwater = SV_CheckWater(ent); if (! wasonground) if (!((int)ent->v.flags & FL_FLY)) if (!(((int)ent->v.flags & FL_SWIM) && (ent->v.waterlevel > 0))) { if (ent->v.velocity[2] < sv_gravity.value*-0.1) hitsound = true; if (!inwater) SV_AddGravity (ent); } if (!VectorCompare(ent->v.velocity, vec_origin) || !VectorCompare(ent->v.basevelocity, vec_origin)) { ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; // apply friction // let dead monsters who aren't completely onground slide if (wasonground) if (!(ent->v.health <= 0.0 && !SV_CheckBottom(ent))) { vel = ent->v.velocity; speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]); if (speed) { friction = sv_friction.value; control = speed < sv_stopspeed.value ? sv_stopspeed.value : speed; newspeed = speed - host_frametime*control*friction; if (newspeed < 0) newspeed = 0; newspeed /= speed; vel[0] = vel[0] * newspeed; vel[1] = vel[1] * newspeed; } } VectorAdd (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); SV_FlyMove (ent, host_frametime, NULL); VectorSubtract (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); // determine if it's on solid ground at all { vec3_t mins, maxs, point; int x, y; VectorAdd (ent->v.origin, ent->v.mins, mins); VectorAdd (ent->v.origin, ent->v.maxs, maxs); point[2] = mins[2] - 1; for (x=0 ; x<=1 ; x++) for (y=0 ; y<=1 ; y++) { point[0] = x ? maxs[0] : mins[0]; point[1] = y ? maxs[1] : mins[1]; if (SV_PointContents (point) == CONTENTS_SOLID) { ent->v.flags = (int)ent->v.flags | FL_ONGROUND; break; } } } SV_LinkEdict (ent, true); if ((int)ent->v.flags & FL_ONGROUND) if (!wasonground) if (hitsound) SV_StartSound (ent, 0, "demon/dland2.wav", 255, 1); } // regular thinking SV_RunThink (ent); SV_CheckWaterTransition (ent); }
/* ============= SV_Physics_Toss Toss, bounce, and fly movement. When onground, do nothing. ============= */ void SV_Physics_Toss (edict_t *ent) { trace_t trace; vec3_t move; float backoff; #ifdef QUAKE2 edict_t *groundentity; groundentity = PROG_TO_EDICT(ent->v.groundentity); if ((int)groundentity->v.flags & FL_CONVEYOR) VectorScale(groundentity->v.movedir, groundentity->v.speed, ent->v.basevelocity); else VectorCopy(vec_origin, ent->v.basevelocity); SV_CheckWater (ent); #endif // regular thinking if (!SV_RunThink (ent)) return; #ifdef QUAKE2 if (ent->v.velocity[2] > 0) ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; if ( ((int)ent->v.flags & FL_ONGROUND) ) //@@ if (VectorCompare(ent->v.basevelocity, vec_origin)) return; SV_CheckVelocity (ent); // add gravity if (! ((int)ent->v.flags & FL_ONGROUND) && ent->v.movetype != MOVETYPE_FLY && ent->v.movetype != MOVETYPE_BOUNCEMISSILE && ent->v.movetype != MOVETYPE_FLYMISSILE) SV_AddGravity (ent); #else // if onground, return without moving if ( ((int)ent->v.flags & FL_ONGROUND) ) return; SV_CheckVelocity (ent); // add gravity if (ent->v.movetype != MOVETYPE_FLY && ent->v.movetype != MOVETYPE_FLYMISSILE) SV_AddGravity (ent); #endif // move angles VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles); // move origin #ifdef QUAKE2 VectorAdd (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); #endif VectorScale (ent->v.velocity, host_frametime, move); trace = SV_PushEntity (ent, move); #ifdef QUAKE2 VectorSubtract (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); #endif if (trace.fraction == 1) return; if (ent->free) return; if (ent->v.movetype == MOVETYPE_BOUNCE) backoff = 1.5; #ifdef QUAKE2 else if (ent->v.movetype == MOVETYPE_BOUNCEMISSILE) backoff = 2.0; #endif else backoff = 1; ClipVelocity (ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff); // stop if on ground if (trace.plane.normal[2] > 0.7) { #ifdef QUAKE2 if (ent->v.velocity[2] < 60 || (ent->v.movetype != MOVETYPE_BOUNCE && ent->v.movetype != MOVETYPE_BOUNCEMISSILE)) #else if (ent->v.velocity[2] < 60 || ent->v.movetype != MOVETYPE_BOUNCE) #endif { ent->v.flags = (int)ent->v.flags | FL_ONGROUND; ent->v.groundentity = ((int)EDICT_TO_PROG(trace.ent)); VectorCopy (vec3_origin, ent->v.velocity); VectorCopy (vec3_origin, ent->v.avelocity); } } // check for in water SV_CheckWaterTransition (ent); }
/* ================ SV_Physics_Client Player character actions ================ */ void SV_Physics_Client (edict_t *ent, int num) { if ( ! svs.clients[num-1].active ) return; // unconnected slot // // call standard client pre-think // pr_global_struct->time = sv.time; pr_global_struct->self = ((int)EDICT_TO_PROG(ent)); PR_ExecuteProgram (pr_global_struct->PlayerPreThink); // // do a move // SV_CheckVelocity (ent); // // decide which move function to call // switch ((int)ent->v.movetype) { case MOVETYPE_NONE: if (!SV_RunThink (ent)) return; break; case MOVETYPE_WALK: if (!SV_RunThink (ent)) return; if (!SV_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) ) SV_AddGravity (ent); SV_CheckStuck (ent); #ifdef QUAKE2 VectorAdd (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); #endif SV_WalkMove (ent); #ifdef QUAKE2 VectorSubtract (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); #endif break; case MOVETYPE_TOSS: case MOVETYPE_BOUNCE: SV_Physics_Toss (ent); break; case MOVETYPE_FLY: if (!SV_RunThink (ent)) return; SV_FlyMove (ent, host_frametime, NULL); break; case MOVETYPE_NOCLIP: if (!SV_RunThink (ent)) return; VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin); break; default: Sys_Error ("SV_Physics_client: bad movetype %i", (int)ent->v.movetype); } // // call standard player post-think // SV_LinkEdict (ent, true); pr_global_struct->time = sv.time; pr_global_struct->self = ((int)EDICT_TO_PROG(ent)); PR_ExecuteProgram (pr_global_struct->PlayerPostThink); }
/* ============= SV_Physics_Step Monsters freefall when they don't have a ground entity, otherwise all movement is done with discrete steps. This is also used for objects that have become still on the ground, but will fall if the floor is pulled out from under them. ============= */ void SV_Physics_Step( edict_t *ent ) { qboolean inwater; qboolean wasonground; qboolean wasonmover; vec3_t mins, maxs; vec3_t point; trace_t trace; int x, y; SV_WaterMove( ent ); SV_CheckVelocity( ent ); wasonground = (ent->v.flags & FL_ONGROUND); wasonmover = SV_CheckMover( ent ); inwater = SV_CheckWater( ent ); if( ent->v.flags & FL_FLOAT && ent->v.waterlevel > 0 ) { float buoyancy = SV_Submerged( ent ) * ent->v.skin * host.frametime; SV_AddGravity( ent ); ent->v.velocity[2] += buoyancy; } if( !wasonground && !( ent->v.flags & FL_FLY ) && (!( ent->v.flags & FL_SWIM ) || ent->v.waterlevel <= 0 )) { if( !inwater ) SV_AddGravity( ent ); } if( !VectorIsNull( ent->v.velocity ) || !VectorIsNull( ent->v.basevelocity )) { ent->v.flags &= ~FL_ONGROUND; if(( wasonground || wasonmover ) && ( ent->v.health > 0 || SV_CheckBottom( ent, MOVE_NORMAL ))) { float *vel = ent->v.velocity; float control, speed, newspeed; float friction; speed = sqrt(( vel[0] * vel[0] ) + ( vel[1] * vel[1] )); // DotProduct2D if( speed ) { friction = sv_friction->value * ent->v.friction; // factor ent->v.friction = 1.0f; // g-cont. ??? if( wasonmover ) friction *= 0.5f; // add a little friction control = (speed < sv_stopspeed->value) ? sv_stopspeed->value : speed; newspeed = speed - (host.frametime * control * friction); if( newspeed < 0 ) newspeed = 0; newspeed /= speed; vel[0] = vel[0] * newspeed; vel[1] = vel[1] * newspeed; } } VectorAdd( ent->v.velocity, ent->v.basevelocity, ent->v.velocity ); SV_CheckVelocity( ent ); SV_FlyMove( ent, host.frametime, NULL ); if( ent->free ) return; SV_CheckVelocity( ent ); VectorSubtract( ent->v.velocity, ent->v.basevelocity, ent->v.velocity ); SV_CheckVelocity( ent ); VectorAdd( ent->v.origin, ent->v.mins, mins ); VectorAdd( ent->v.origin, ent->v.maxs, maxs ); point[2] = mins[2] - 1.0f; for( x = 0; x <= 1; x++ ) { if( ent->v.flags & FL_ONGROUND ) break; for( y = 0; y <= 1; y++ ) { point[0] = x ? maxs[0] : mins[0]; point[1] = y ? maxs[1] : mins[1]; trace = SV_Move( point, vec3_origin, vec3_origin, point, MOVE_NORMAL, ent ); if( trace.startsolid ) { ent->v.flags |= FL_ONGROUND; ent->v.groundentity = trace.ent; ent->v.friction = 1.0f; break; } } } SV_LinkEdict( ent, true ); } else { if( svgame.globals->force_retouch != 0 ) { trace = SV_Move( ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, MOVE_NORMAL, ent ); // hentacle impact code if(( trace.fraction < 1.0f || trace.startsolid ) && SV_IsValidEdict( trace.ent )) { SV_Impact( ent, trace.ent, &trace ); if( ent->free ) return; } } } if( !SV_RunThink( ent )) return; SV_CheckWaterTransition( ent ); }
/* ============= SV_Physics_Toss Toss, bounce, and fly movement. When onground, do nothing. ============= */ void SV_Physics_Toss( edict_t *ent ) { trace_t trace; vec3_t move; float backoff; edict_t *ground; SV_CheckWater( ent ); // regular thinking if( !SV_RunThink( ent )) return; ground = ent->v.groundentity; if( ent->v.velocity[2] > 0.0f || !SV_IsValidEdict( ground ) || ground->v.flags & (FL_MONSTER|FL_CLIENT) || svgame.globals->changelevel ) { ent->v.flags &= ~FL_ONGROUND; } // if on ground and not moving, return. if( ent->v.flags & FL_ONGROUND && VectorIsNull( ent->v.velocity )) { VectorClear( ent->v.avelocity ); if( VectorIsNull( ent->v.basevelocity )) return; // at rest } SV_CheckVelocity( ent ); // add gravity switch( ent->v.movetype ) { case MOVETYPE_FLY: case MOVETYPE_FLYMISSILE: case MOVETYPE_BOUNCEMISSILE: break; default: SV_AddGravity( ent ); break; } // move angles (with friction) switch( ent->v.movetype ) { case MOVETYPE_TOSS: case MOVETYPE_BOUNCE: SV_AngularMove( ent, host.frametime, ent->v.friction ); break; default: SV_AngularMove( ent, host.frametime, 0.0f ); break; } // move origin // Base velocity is not properly accounted for since this entity will move again // after the bounce without taking it into account VectorAdd( ent->v.velocity, ent->v.basevelocity, ent->v.velocity ); SV_CheckVelocity( ent ); VectorScale( ent->v.velocity, host.frametime, move ); VectorSubtract( ent->v.velocity, ent->v.basevelocity, ent->v.velocity ); trace = SV_PushEntity( ent, move, vec3_origin, NULL ); if( ent->free ) return; SV_CheckVelocity( ent ); if( trace.allsolid ) { // entity is trapped in another solid VectorClear( ent->v.avelocity ); VectorClear( ent->v.velocity ); return; } if( trace.fraction == 1.0f ) { SV_CheckWaterTransition( ent ); return; } if( ent->v.movetype == MOVETYPE_BOUNCE ) backoff = 2.0f - ent->v.friction; else if( ent->v.movetype == MOVETYPE_BOUNCEMISSILE ) backoff = 2.0f; else backoff = 1.0f; SV_ClipVelocity( ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff ); // stop if on ground if( trace.plane.normal[2] > 0.7f ) { float vel; VectorAdd( ent->v.velocity, ent->v.basevelocity, move ); vel = DotProduct( move, move ); if( ent->v.velocity[2] < sv_gravity->value * host.frametime ) { // we're rolling on the ground, add static friction. ent->v.groundentity = trace.ent; ent->v.flags |= FL_ONGROUND; ent->v.velocity[2] = 0.0f; } if( vel < 900.0f || ( ent->v.movetype != MOVETYPE_BOUNCE && ent->v.movetype != MOVETYPE_BOUNCEMISSILE )) { ent->v.flags |= FL_ONGROUND; ent->v.groundentity = trace.ent; VectorClear( ent->v.avelocity ); VectorClear( ent->v.velocity ); } else { VectorScale( ent->v.velocity, (1.0f - trace.fraction) * host.frametime * 0.9f, move ); VectorMA( move, (1.0f - trace.fraction) * host.frametime * 0.9f, ent->v.basevelocity, move ); trace = SV_PushEntity( ent, move, vec3_origin, NULL ); if( ent->free ) return; } } // check for in water SV_CheckWaterTransition( ent ); }
/* ================ SV_Physics_Client Player character actions ================ */ void SV_Physics_Client (edict_t *ent, int num) { client_t *cl; vec3_t v; qboolean was_angle_set; cl = &svs.clients[num-1]; if (!cl->active) return; // unconnected slot // call standard client pre-think PR_GLOBAL(time) = sv.time; PR_GLOBAL(self) = EDICT_TO_PROG(ent); PR_ExecuteProgram (PR_GLOBAL(PlayerPreThink)); // for cutscene hack (see below) if (isDedicated || (num != 1)) was_angle_set = false; // do it on local client only else was_angle_set = (ent->v.fixangle != 0); // do a move SV_CheckVelocity (ent); // decide which move function to call switch ((int)ent->v.movetype) { case MOVETYPE_NONE: if (!SV_RunThink(ent)) return; break; case MOVETYPE_WALK: if (!SV_RunThink(ent)) return; if (!SV_CheckWater(ent) && !((int)ent->v.flags & FL_WATERJUMP)) SV_AddGravity (ent); SV_CheckStuck (ent); SV_WalkMove (ent); break; case MOVETYPE_TOSS: case MOVETYPE_BOUNCE: SV_Physics_Toss (ent); break; case MOVETYPE_FLY: #ifdef HEXEN2_SUPPORT case MOVETYPE_SWIM: #endif if (!SV_RunThink(ent)) return; SV_FlyMove (ent, host_frametime, NULL); break; case MOVETYPE_NOCLIP: if (!SV_RunThink(ent)) return; VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin); break; default: Sys_Error ("SV_Physics_client: bad movetype %i", (int)ent->v.movetype); } // JDH: hack for cutscenes made by Darin McNeil's Cutscene Construction Kit: // (note that the extra precision is noticeable only if the viewangles // are sent from server to client as 2-byte values; hence the addition // of the new svc_setpreciseangle message code) if (was_angle_set && (ent->v.view_ofs[2] == 0) && host_cutscenehack.value && !strcmp (pr_strings + ent->v.classname, "camera")) { // - when camera changes back to player, classname remains "camera" for // 1 frame, but movedir is no longer valid. So as an additional check, // I verify that view_ofs[2] is still 0 // - early version(s?) of Cutscene Construction Kit don't move the camera, // so movedir is not used. I determine the version by checking *when* // the viewangle is set: early version does it in the .think function; // later ones in PlayerPreThink. was_angle_set will be true only if // it was changed in PlayerPreThink //if (!sv_oldprotocol.value) { v[0] = ent->v.movedir[0] - ent->v.origin[0]; v[1] = ent->v.movedir[1] - ent->v.origin[1]; v[2] = ent->v.origin[2] - ent->v.movedir[2]; //vectoangles (v, ent->v.angles); vectoangles (v, cl->cutscene_viewangles); } if (!cl->in_cutscene) { int i; edict_t *ed; // by this time, the player's viewangles have already been changed. // But the dummy entity spawned in place of the player has the values for (i = 1 ; i < sv.num_edicts ; i++) { // get the current server version ed = EDICT_NUM(i); if (ed->free) continue; if (!strcmp(pr_strings + ed->v.classname, "dummy")) { VectorCopy (ed->v.angles, cl->prev_viewangles); break; } } cl->in_cutscene = true; } //sv.found_cutscene = true; } else { if (cl->in_cutscene) { // I'm not sure why, but last viewangle while in_cutscene isn't final angle ent->v.fixangle = 1; VectorCopy (cl->prev_viewangles, ent->v.angles); cl->in_cutscene = false; } } SV_LinkEdict (ent, true); PR_GLOBAL(time) = sv.time; PR_GLOBAL(self) = EDICT_TO_PROG(ent); // JDH: another hack, this time for progs that lack CycleWeaponReverse if ((ent->v.impulse == 12.0) && ((sv_imp12hack.value >= 2) || (sv_imp12hack.value && !pr_handles_imp12)) && !ent->v.deadflag && (ent->v.view_ofs[0] || ent->v.view_ofs[1] || ent->v.view_ofs[2])) { eval_t *val = GETEDICTFIELD(ent, eval_attack_finished); if (val && (sv.time >= val->_float)) { SV_CycleWeaponReverse (ent); } } // call standard player post-think PR_ExecuteProgram (PR_GLOBAL(PlayerPostThink)); }
/* ================ SV_Physics ================ */ void SV_Physics (void) { int i; int entity_cap; // For sv_freezenonclients edict_t *ent; // let the progs know that a new frame has started pr_global_struct->self = EDICT_TO_PROG(sv.edicts); pr_global_struct->other = EDICT_TO_PROG(sv.edicts); pr_global_struct->time = sv.time; PR_ExecuteProgram (pr_global_struct->StartFrame); //SV_CheckAllEnts (); // // treat each object in turn // ent = sv.edicts; if (sv.frozen) entity_cap = svs.maxclients + 1; // Only run physics on clients and the world else entity_cap = sv.num_edicts; for (i=0 ; i<entity_cap ; i++, ent = NEXT_EDICT(ent)) // for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent)) { if (ent->free) continue; if (pr_global_struct->force_retouch) { SV_LinkEdict (ent, true); // force retouch even for stationary } if (i > 0 && i <= svs.maxclients) SV_Physics_Client (ent, i); else if (ent->v.movetype == MOVETYPE_PUSH) SV_Physics_Pusher (ent); else if (ent->v.movetype == MOVETYPE_NONE) SV_Physics_None (ent); else if (ent->v.movetype == MOVETYPE_FOLLOW) // Nehahra SV_Physics_Follow (ent); else if (ent->v.movetype == MOVETYPE_NOCLIP) SV_Physics_Noclip (ent); else if (ent->v.movetype == MOVETYPE_STEP) SV_Physics_Step (ent); else if (ent->v.movetype == MOVETYPE_WALK) // Nehahra { if (!SV_RunThink (ent)) return; if (!SV_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) ) SV_AddGravity (ent); SV_CheckStuck (ent); SV_WalkMove (ent); } else if (ent->v.movetype == MOVETYPE_TOSS || ent->v.movetype == MOVETYPE_BOUNCE || ent->v.movetype == MOVETYPE_FLY || ent->v.movetype == MOVETYPE_FLYMISSILE) SV_Physics_Toss (ent); else Host_Error ("SV_Physics: bad movetype %i", (int)ent->v.movetype); } if (pr_global_struct->force_retouch) pr_global_struct->force_retouch--; if (!sv.frozen) sv.time += host_frametime; }
/* ================ SV_Physics_Client Player character actions ================ */ void SV_Physics_Client (edict_t *ent, int num) { if ( ! svs.clients[num-1].active ) return; // unconnected slot // // call standard client pre-think // pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(ent); PR_ExecuteProgram (pr_global_struct->PlayerPreThink); // // do a move // SV_CheckVelocity (ent); // // decide which move function to call // switch ((int)ent->v.movetype) { case MOVETYPE_NONE: if (!SV_RunThink (ent)) return; break; case MOVETYPE_WALK: if (!SV_RunThink (ent)) return; if (!SV_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) ) SV_AddGravity (ent); SV_CheckStuck (ent); SV_WalkMove (ent); break; case MOVETYPE_TOSS: case MOVETYPE_BOUNCE: SV_Physics_Toss (ent); break; case MOVETYPE_FLY: if (!SV_RunThink (ent)) return; SV_CheckWater (ent); SV_FlyMove (ent, host_frametime, NULL); break; case MOVETYPE_NOCLIP: if (!SV_RunThink (ent)) return; ent->v.waterlevel = 0; // Avoid annoying waterjumps in noclip ent->v.watertype = CONTENTS_EMPTY; // Avoid annoying waterjumps in noclip VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin); break; default: Host_Error ("SV_Physics_client: bad movetype %i", (int)ent->v.movetype); } // // call standard player post-think // if (ent->v.movetype == MOVETYPE_NOCLIP) SV_LinkEdict (ent, false); // don't touch triggers in noclip else SV_LinkEdict (ent, true); pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(ent); PR_ExecuteProgram (pr_global_struct->PlayerPostThink); }