/* ============== V_CalcViewRoll Roll is induced by movement and damage ============== */ void V_CalcViewRoll (void) { float side; float adjspeed; side = V_CalcRoll (cl.simangles, cl.simvel); adjspeed = 20 * bound (2, fabs(cl_rollangle.value), 45); if (side > cl.rollangle) { cl.rollangle += cls.frametime * adjspeed; if (cl.rollangle > side) cl.rollangle = side; } else if (side < cl.rollangle) { cl.rollangle -= cls.frametime * adjspeed; if (cl.rollangle < side) cl.rollangle = side; } r_refdef2.viewangles[ROLL] += cl.rollangle; if (v_dmg_time > 0 && v_kicktime.value > 0) { r_refdef2.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll; r_refdef2.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch; v_dmg_time -= cls.frametime; } }
/* =================== SV_ClientThink the move fields specify an intended velocity in pix/sec the angle fields specify an exact angular motion in degrees =================== */ void SV_ClientThink (void) { vec3_t v_angle; if (sv_player->v.movetype == MOVETYPE_NONE) return; onground = (int)sv_player->v.flags & FL_ONGROUND; origin = sv_player->v.origin; velocity = sv_player->v.velocity; DropPunchAngle (); // // if dead, behave differently // if (sv_player->v.health <= 0) return; // // angles // show 1/3 the pitch angle and all the roll angle cmd = host_client->cmd; angles = sv_player->v.angles; VectorAdd (sv_player->v.v_angle, sv_player->v.punchangle, v_angle); angles[ROLL] = V_CalcRoll (sv_player->v.angles, sv_player->v.velocity)*4; if (!sv_player->v.fixangle) { angles[PITCH] = -v_angle[PITCH]/3; angles[YAW] = v_angle[YAW]; } if ( (int)sv_player->v.flags & FL_WATERJUMP ) { SV_WaterJump (); return; } // // walk // if ( (sv_player->v.waterlevel >= 2) && (sv_player->v.movetype != MOVETYPE_NOCLIP) ) { SV_WaterMove (); return; } SV_AirMove (); }
void V_CalcViewRoll ( struct ref_params_s *pparams ) { float side; cl_entity_t *viewentity; viewentity = gEngfuncs.GetEntityByIndex( pparams->viewentity ); if ( !viewentity ) return; // Fograin92: cl_rollangle/cl_rollspeed feature restoration. (Thanks to: L453rh4wk) pparams->viewangles[ROLL] = V_CalcRoll (pparams->viewangles, pparams->simvel, cl_rollangle->value, cl_rollspeed->value ) * 4; side = V_CalcRoll ( viewentity->angles, pparams->simvel, pparams->movevars->rollangle, pparams->movevars->rollspeed ); pparams->viewangles[ROLL] += side; if ( pparams->health <= 0 && ( pparams->viewheight[2] != 0 ) ) { // only roll the view if the player is dead and the viewheight[2] is nonzero // this is so deadcam in multiplayer will work. pparams->viewangles[ROLL] = 80; // dead view angle return; } }
/* ============== V_CalcViewRoll Roll is induced by movement and damage ============== */ void V_CalcViewRoll ( struct ref_params_s *pparams ) { cl_entity_t *viewentity; viewentity = gEngfuncs.GetEntityByIndex( pparams->viewentity ); if ( !viewentity ) return; //Roll the angles when strafing Quake style! pparams->viewangles[ROLL] = V_CalcRoll (pparams->viewangles, pparams->simvel, cl_rollangle->value, cl_rollspeed->value ) * 4; if ( pparams->health <= 0 && ( pparams->viewheight[2] != 0 ) ) { // only roll the view if the player is dead and the viewheight[2] is nonzero // this is so deadcam in multiplayer will work. pparams->viewangles[ROLL] = 80; // dead view angle return; } }
/* ============== V_CalcViewRoll Roll is induced by movement and damage ============== */ void V_CalcViewRoll (void) { float side; side = V_CalcRoll (cl_entities[cl.viewentity].angles, cl.velocity); r_refdef.viewangles[ROLL] += side; if (v_dmg_time > 0) { r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll; r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch; v_dmg_time -= host_frametime; } if (cl.stats[STAT_HEALTH] <= 0) { r_refdef.viewangles[ROLL] = 80; // dead view angle return; } }
/* ============== V_CalcViewRoll Roll is induced by movement and damage ============== */ void V_CalcViewRoll(struct ref_params_s *pparams) { float side; cl_entity_t *viewentity; viewentity = gEngfuncs.GetEntityByIndex(pparams->viewentity); if(!viewentity) return; side = V_CalcRoll(viewentity->angles, pparams->simvel, pparams->movevars->rollangle, pparams->movevars->rollspeed); pparams->viewangles[ROLL] += side; if(pparams->health <= 0 && (pparams->viewheight[2] != 0)) { // only roll the view if the player is dead and the viewheight[2] is nonzero // this is so deadcam in multiplayer will work. pparams->viewangles[ROLL] = 80; // dead view angle return; } }
/* V_CalcViewRoll Roll is induced by movement and damage */ static void V_CalcViewRoll (void) { float side; vec_t *angles = cl.simangles; vec_t *velocity = cl.simvel; side = V_CalcRoll (angles, velocity); r_data->refdef->viewangles[ROLL] += side; if (v_dmg_time > 0) { r_data->refdef->viewangles[ROLL] += v_dmg_time / v_kicktime->value * v_dmg_roll; r_data->refdef->viewangles[PITCH] += v_dmg_time / v_kicktime->value * v_dmg_pitch; v_dmg_time -= host_frametime; } if (view_message->pls.flags & PF_DEAD) // PF_GIB will also set PF_DEAD r_data->refdef->viewangles[ROLL] = 80; // dead view angle }
/* =================== SV_ClientThink the move fields specify an intended velocity in pix/sec the angle fields specify an exact angular motion in degrees =================== */ static void SV_ClientThink(client_t *client) { edict_t *player = client->edict; vec3_t v_angle; if (player->v.movetype == MOVETYPE_NONE) return; DropPunchAngle(player->v.punchangle); /* if dead, behave differently */ if (player->v.health <= 0) return; /* angles - show 1/3 the pitch angle and all the roll angle */ VectorAdd(player->v.v_angle, player->v.punchangle, v_angle); player->v.angles[ROLL] = V_CalcRoll(player->v.angles, player->v.velocity) * 4; if (!player->v.fixangle) { player->v.angles[PITCH] = -v_angle[PITCH] / 3; player->v.angles[YAW] = v_angle[YAW]; } if ((int)player->v.flags & FL_WATERJUMP) { SV_WaterJump(player); return; } /* walk */ if (player->v.waterlevel >= 2 && player->v.movetype != MOVETYPE_NOCLIP) { SV_WaterMove(&client->cmd, player); return; } SV_AirMove(&client->cmd, player); }
/* ============= CL_LinkPlayers Create visible entities in the correct position for all current players ============= */ void CL_LinkPlayers (void) { int j; player_info_t *info; player_state_t *state; player_state_t exact; double playertime; entity_t *ent; int msec; frame_t *frame; int oldphysent; playertime = realtime - cls.latency + 0.02; if (playertime > realtime) playertime = realtime; frame = &cl.frames[cl.parsecount&UPDATE_MASK]; for (j=0, info=cl.players, state=frame->playerstate ; j < MAX_CLIENTS ; j++, info++, state++) { if (state->messagenum != cl.parsecount) continue; // not present this frame // spawn light flashes, even ones coming from invisible objects // #ifdef GLQUAKE // if (!gl_flashblend.value || j != cl.playernum) { // #endif if ((state->effects & (EF_BLUE | EF_RED)) == (EF_BLUE | EF_RED)) CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2], 200 + (rand()&31), 0.1, 3); else if (state->effects & EF_BLUE) CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2], 200 + (rand()&31), 0.1, 1); else if (state->effects & EF_RED) CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2], 200 + (rand()&31), 0.1, 2); else if (state->effects & EF_BRIGHTLIGHT) CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2] + 16, 400 + (rand()&31), 0.1, 0); else if (state->effects & EF_DIMLIGHT) CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2], 200 + (rand()&31), 0.1, 0); // #ifdef GLQUAKE // } // #endif // the player object never gets added if (j == cl.playernum) continue; if (!state->modelindex) continue; if (!Cam_DrawPlayer(j)) continue; // grab an entity to fill in if (cl_numvisedicts == MAX_VISEDICTS) break; // object list is full ent = &cl_visedicts[cl_numvisedicts]; cl_numvisedicts++; ent->keynum = 0; ent->model = cl.model_precache[state->modelindex]; ent->skinnum = state->skinnum; ent->frame = state->frame; ent->colormap = info->translations; if (state->modelindex == cl_playerindex) ent->scoreboard = info; // use custom skin else ent->scoreboard = NULL; // // angles // ent->angles[PITCH] = -state->viewangles[PITCH]/3; ent->angles[YAW] = state->viewangles[YAW]; ent->angles[ROLL] = 0; ent->angles[ROLL] = V_CalcRoll (ent->angles, state->velocity)*4; // only predict half the move to minimize overruns msec = 500*(playertime - state->state_time); if (msec <= 0 || (!cl_predict_players.value && !cl_predict_players2.value)) { VectorCopy (state->origin, ent->origin); //Con_DPrintf ("nopredict\n"); } else { // predict players movement if (msec > 255) msec = 255; state->command.msec = msec; //Con_DPrintf ("predict: %i\n", msec); oldphysent = pmove.numphysent; CL_SetSolidPlayers (j); CL_PredictUsercmd (state, &exact, &state->command, false); pmove.numphysent = oldphysent; VectorCopy (exact.origin, ent->origin); } if (state->effects & EF_FLAG1) CL_AddFlagModels (ent, 0); else if (state->effects & EF_FLAG2) CL_AddFlagModels (ent, 1); } }
void SV_ClientThink (void) { vec3_t v_angle; int yeahdead; if (sv_player->v.movetype == MOVETYPE_NONE) return; if (cl.stats[STAT_HEALTH] <= 0) { yeahdead = 1; if (!deathcam_yesiamdead){ deathcam_angles[PITCH] = 50; // deathcam_angles[YAW] = 20; deathcam_angles[ROLL] = 0; deathcam_whenidied = sv.time; //Con_Printf("I died at %f. sob.\n", deathcam_whenidied); } if (cl_diecam->value) deathcam_yesiamdead = cl_diecam->value; } else { yeahdead = 0; deathcam_yesiamdead = 0; } onground = (int)sv_player->v.flags & FL_ONGROUND; origin = sv_player->v.origin; velocity = sv_player->v.velocity; DropPunchAngle (); // // if dead, behave differently // // leilei - standstill hack if (sv_standstill->value) { float sample1, sample2, sample3, sample4, sample5; float divided; if (sv_maxspeed->value) divided = 1 / sv_maxspeed->value * sv_standstill->value; else divided = 1; // avoiding a div0...... sample1 = cmd.forwardmove * divided; sample2 = cmd.sidemove * divided; sample3 = cmd.upmove * divided; sample4 = amouse_x * divided; sample5 = amouse_y * divided; if (sample1 < 0) sample1 *= -1; if (sample2 < 0) sample2 *= -1; if (sample3 < 0) sample3 *= -1; if (sample4 < 0) sample4 *= -1; if (sample5 < 0) sample5 *= -1; thestandstill = sample1 + sample2 + sample3 + sample4 + sample5 * 0.5; if (thestandstill > 1) thestandstill = 1; Cvar_SetValue (host_timescale,thestandstill); // slow it down! } if (sv_player->v.health <= 0) return; // // angles // show 1/3 the pitch angle and all the roll angle cmd = host_client->cmd; angles = sv_player->v.angles; // leilei - aim lock if (aimlock){ if ((lockedangle[YAW] + aimlockangle) > 360){ // Con_Printf("goddamnit.\n"); if (cl.viewangles[YAW] > (lockedangle[YAW] + aimlockangle > 360)) sv_player->v.angles[YAW] -= 360; if (cl.viewangles[YAW] > lockedangle[YAW] + aimlockangle) cl.viewangles[YAW] = lockedangle[YAW] + aimlockangle; } else if (cl.viewangles[YAW] > lockedangle[YAW] + aimlockangle) cl.viewangles[YAW] = lockedangle[YAW] + aimlockangle; if ((lockedangle[YAW] - aimlockangle) < 0){ if (cl.viewangles[YAW] < lockedangle[YAW] - aimlockangle) cl.viewangles[YAW] = lockedangle[YAW] - aimlockangle + 360; if (cl.viewangles[YAW] < lockedangle[YAW] - aimlockangle) cl.viewangles[YAW] = lockedangle[YAW] - aimlockangle; // Con_Printf("mother\n"); } else if (cl.viewangles[YAW] < lockedangle[YAW] - aimlockangle) cl.viewangles[YAW] = lockedangle[YAW] - aimlockangle; // TODO: wrap angle, wrapangle if (cl.viewangles[PITCH] < lockedangle[PITCH] - aimlockangle) cl.viewangles[PITCH] = lockedangle[PITCH] - aimlockangle; if (cl.viewangles[PITCH] > lockedangle[PITCH] + aimlockangle) cl.viewangles[PITCH] = lockedangle[PITCH] + aimlockangle; // if (cl.viewangles[YAW] < lockedangle[YAW] - aimlockangle) cl.viewangles[YAW] = lockedangle[YAW] - aimlockangle; // if (cl.viewangles[YAW] > lockedangle[YAW] + aimlockangle) cl.viewangles[YAW] = lockedangle[YAW] + aimlockangle; } VectorAdd (sv_player->v.v_angle, sv_player->v.punchangle, v_angle); angles[ROLL] = V_CalcRoll (sv_player->v.angles, sv_player->v.velocity)*4; if (!sv_player->v.fixangle) { angles[PITCH] = -v_angle[PITCH]/3; angles[YAW] = v_angle[YAW]; } if ( (int)sv_player->v.flags & FL_WATERJUMP ) { SV_WaterJump (); return; } // // walk // if ( (sv_player->v.waterlevel >= 2) && (sv_player->v.movetype != MOVETYPE_NOCLIP) ) { SV_WaterMove (); return; } SV_AirMove (); }
/* * State: * cl.bob2_smooth * cl.bobfall_speed * cl.bobfall_swing * cl.gunangles_adjustment_highpass * cl.gunangles_adjustment_lowpass * cl.gunangles_highpass * cl.gunangles_prev * cl.gunorg_adjustment_highpass * cl.gunorg_adjustment_lowpass * cl.gunorg_highpass * cl.gunorg_prev * cl.hitgroundtime * cl.lastongroundtime * cl.oldongrounbd * cl.stairsmoothtime * cl.stairsmoothz * cl.calcrefdef_prevtime * Extra input: * cl.movecmd[0].time * cl.movevars_stepheight * cl.movevars_timescale * cl.oldtime * cl.punchangle * cl.punchvector * cl.qw_intermission_angles * cl.qw_intermission_origin * cl.qw_weaponkick * cls.protocol * cl.time * Output: * cl.csqc_viewanglesfromengine * cl.csqc_viewmodelmatrixfromengine * cl.csqc_vieworiginfromengine * r_refdef.view.matrix * viewmodelmatrix_nobob * viewmodelmatrix_withbob */ void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewangles, qboolean teleported, qboolean clonground, qboolean clcmdjump, float clstatsviewheight, qboolean cldead, qboolean clintermission, const vec3_t clvelocity) { float vieworg[3], viewangles[3], smoothtime; float gunorg[3], gunangles[3]; matrix4x4_t tmpmatrix; static float viewheightavg; float viewheight; #if 0 // begin of chase camera bounding box size for proper collisions by Alexander Zubov vec3_t camboxmins = {-3, -3, -3}; vec3_t camboxmaxs = {3, 3, 3}; // end of chase camera bounding box size for proper collisions by Alexander Zubov #endif trace_t trace; // react to clonground state changes (for gun bob) if (clonground) { if (!cl.oldonground) cl.hitgroundtime = cl.movecmd[0].time; cl.lastongroundtime = cl.movecmd[0].time; } cl.oldonground = clonground; cl.calcrefdef_prevtime = max(cl.calcrefdef_prevtime, cl.oldtime); VectorClear(gunorg); viewmodelmatrix_nobob = identitymatrix; viewmodelmatrix_withbob = identitymatrix; r_refdef.view.matrix = identitymatrix; // player can look around, so take the origin from the entity, // and the angles from the input system Matrix4x4_OriginFromMatrix(entrendermatrix, vieworg); VectorCopy(clviewangles, viewangles); // calculate how much time has passed since the last V_CalcRefdef smoothtime = bound(0, cl.time - cl.stairsmoothtime, 0.1); cl.stairsmoothtime = cl.time; // fade damage flash if (v_dmg_time > 0) v_dmg_time -= bound(0, smoothtime, 0.1); if (clintermission) { // entity is a fixed camera, just copy the matrix if (cls.protocol == PROTOCOL_QUAKEWORLD) Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, cl.qw_intermission_origin[0], cl.qw_intermission_origin[1], cl.qw_intermission_origin[2], cl.qw_intermission_angles[0], cl.qw_intermission_angles[1], cl.qw_intermission_angles[2], 1); else { r_refdef.view.matrix = *entrendermatrix; Matrix4x4_AdjustOrigin(&r_refdef.view.matrix, 0, 0, clstatsviewheight); } Matrix4x4_Copy(&viewmodelmatrix_nobob, &r_refdef.view.matrix); Matrix4x4_ConcatScale(&viewmodelmatrix_nobob, cl_viewmodel_scale.value); Matrix4x4_Copy(&viewmodelmatrix_withbob, &viewmodelmatrix_nobob); VectorCopy(vieworg, cl.csqc_vieworiginfromengine); VectorCopy(viewangles, cl.csqc_viewanglesfromengine); Matrix4x4_Invert_Simple(&tmpmatrix, &r_refdef.view.matrix); Matrix4x4_CreateScale(&cl.csqc_viewmodelmatrixfromengine, cl_viewmodel_scale.value); } else { // smooth stair stepping, but only if clonground and enabled if (!clonground || cl_stairsmoothspeed.value <= 0 || teleported) cl.stairsmoothz = vieworg[2]; else { if (cl.stairsmoothz < vieworg[2]) vieworg[2] = cl.stairsmoothz = bound(vieworg[2] - cl.movevars_stepheight, cl.stairsmoothz + smoothtime * cl_stairsmoothspeed.value, vieworg[2]); else if (cl.stairsmoothz > vieworg[2]) vieworg[2] = cl.stairsmoothz = bound(vieworg[2], cl.stairsmoothz - smoothtime * cl_stairsmoothspeed.value, vieworg[2] + cl.movevars_stepheight); } // apply qw weapon recoil effect (this did not work in QW) // TODO: add a cvar to disable this viewangles[PITCH] += cl.qw_weaponkick; // apply the viewofs (even if chasecam is used) // Samual: Lets add smoothing for this too so that things like crouching are done with a transition. viewheight = bound(0, (cl.time - cl.calcrefdef_prevtime) / max(0.0001, cl_smoothviewheight.value), 1); viewheightavg = viewheightavg * (1 - viewheight) + clstatsviewheight * viewheight; vieworg[2] += viewheightavg; if (chase_active.value) { // observing entity from third person. Added "campitch" by Alexander "motorsep" Zubov vec_t camback, camup, dist, campitch, forward[3], chase_dest[3]; camback = chase_back.value; camup = chase_up.value; campitch = chase_pitchangle.value; AngleVectors(viewangles, forward, NULL, NULL); if (chase_overhead.integer) { #if 1 vec3_t offset; vec3_t bestvieworg; #endif vec3_t up; viewangles[PITCH] = 0; AngleVectors(viewangles, forward, NULL, up); // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range) chase_dest[0] = vieworg[0] - forward[0] * camback + up[0] * camup; chase_dest[1] = vieworg[1] - forward[1] * camback + up[1] * camup; chase_dest[2] = vieworg[2] - forward[2] * camback + up[2] * camup; #if 0 #if 1 //trace = CL_TraceLine(vieworg, eyeboxmins, eyeboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false); trace = CL_TraceLine(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false); #else //trace = CL_TraceBox(vieworg, eyeboxmins, eyeboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false); trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false); #endif VectorCopy(trace.endpos, vieworg); vieworg[2] -= 8; #else // trace from first person view location to our chosen third person view location #if 1 trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false, true); #else trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false); #endif VectorCopy(trace.endpos, bestvieworg); offset[2] = 0; for (offset[0] = -16;offset[0] <= 16;offset[0] += 8) { for (offset[1] = -16;offset[1] <= 16;offset[1] += 8) { AngleVectors(viewangles, NULL, NULL, up); chase_dest[0] = vieworg[0] - forward[0] * camback + up[0] * camup + offset[0]; chase_dest[1] = vieworg[1] - forward[1] * camback + up[1] * camup + offset[1]; chase_dest[2] = vieworg[2] - forward[2] * camback + up[2] * camup + offset[2]; #if 1 trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false, true); #else trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false); #endif if (bestvieworg[2] > trace.endpos[2]) bestvieworg[2] = trace.endpos[2]; } } bestvieworg[2] -= 8; VectorCopy(bestvieworg, vieworg); #endif viewangles[PITCH] = campitch; } else { if (gamemode == GAME_GOODVSBAD2 && chase_stevie.integer) { // look straight down from high above viewangles[PITCH] = 90; camback = 2048; VectorSet(forward, 0, 0, -1); } // trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range) dist = -camback - 8; chase_dest[0] = vieworg[0] + forward[0] * dist; chase_dest[1] = vieworg[1] + forward[1] * dist; chase_dest[2] = vieworg[2] + forward[2] * dist + camup; trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false, true); VectorMAMAM(1, trace.endpos, 8, forward, 4, trace.plane.normal, vieworg); } } else { // first person view from entity // angles if (cldead && v_deathtilt.integer) viewangles[ROLL] = v_deathtiltangle.value; if (cl_weaponrecoil.integer > 0) VectorAdd(viewangles, cl.punchangle, viewangles); viewangles[ROLL] += V_CalcRoll(clviewangles, clvelocity); if (v_dmg_time > 0) { viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll; viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch; } // origin if (cl_weaponrecoil.integer > 0) VectorAdd(vieworg, cl.punchvector, vieworg); if (!cldead) { double xyspeed, bob, bobfall; float cycle; vec_t frametime; frametime = (cl.time - cl.calcrefdef_prevtime) * cl.movevars_timescale; // 1. if we teleported, clear the frametime... the lowpass will recover the previous value then if(teleported) { // try to fix the first highpass; result is NOT // perfect! TODO find a better fix VectorCopy(viewangles, cl.gunangles_prev); VectorCopy(vieworg, cl.gunorg_prev); } // 2. for the gun origin, only keep the high frequency (non-DC) parts, which is "somewhat like velocity" VectorAdd(cl.gunorg_highpass, cl.gunorg_prev, cl.gunorg_highpass); highpass3_limited(vieworg, frametime*cl_followmodel_side_highpass1.value, cl_followmodel_side_limit.value, frametime*cl_followmodel_side_highpass1.value, cl_followmodel_side_limit.value, frametime*cl_followmodel_up_highpass1.value, cl_followmodel_up_limit.value, cl.gunorg_highpass, gunorg); VectorCopy(vieworg, cl.gunorg_prev); VectorSubtract(cl.gunorg_highpass, cl.gunorg_prev, cl.gunorg_highpass); // in the highpass, we _store_ the DIFFERENCE to the actual view angles... VectorAdd(cl.gunangles_highpass, cl.gunangles_prev, cl.gunangles_highpass); cl.gunangles_highpass[PITCH] += 360 * floor((viewangles[PITCH] - cl.gunangles_highpass[PITCH]) / 360 + 0.5); cl.gunangles_highpass[YAW] += 360 * floor((viewangles[YAW] - cl.gunangles_highpass[YAW]) / 360 + 0.5); cl.gunangles_highpass[ROLL] += 360 * floor((viewangles[ROLL] - cl.gunangles_highpass[ROLL]) / 360 + 0.5); highpass3_limited(viewangles, frametime*cl_leanmodel_up_highpass1.value, cl_leanmodel_up_limit.value, frametime*cl_leanmodel_side_highpass1.value, cl_leanmodel_side_limit.value, 0, 0, cl.gunangles_highpass, gunangles); VectorCopy(viewangles, cl.gunangles_prev); VectorSubtract(cl.gunangles_highpass, cl.gunangles_prev, cl.gunangles_highpass); // 3. calculate the RAW adjustment vectors gunorg[0] *= (cl_followmodel.value ? -cl_followmodel_side_speed.value : 0); gunorg[1] *= (cl_followmodel.value ? -cl_followmodel_side_speed.value : 0); gunorg[2] *= (cl_followmodel.value ? -cl_followmodel_up_speed.value : 0); gunangles[PITCH] *= (cl_leanmodel.value ? -cl_leanmodel_up_speed.value : 0); gunangles[YAW] *= (cl_leanmodel.value ? -cl_leanmodel_side_speed.value : 0); gunangles[ROLL] = 0; // 4. perform highpass/lowpass on the adjustment vectors (turning velocity into acceleration!) // trick: we must do the lowpass LAST, so the lowpass vector IS the final vector! highpass3(gunorg, frametime*cl_followmodel_side_highpass.value, frametime*cl_followmodel_side_highpass.value, frametime*cl_followmodel_up_highpass.value, cl.gunorg_adjustment_highpass, gunorg); lowpass3(gunorg, frametime*cl_followmodel_side_lowpass.value, frametime*cl_followmodel_side_lowpass.value, frametime*cl_followmodel_up_lowpass.value, cl.gunorg_adjustment_lowpass, gunorg); // we assume here: PITCH = 0, YAW = 1, ROLL = 2 highpass3(gunangles, frametime*cl_leanmodel_up_highpass.value, frametime*cl_leanmodel_side_highpass.value, 0, cl.gunangles_adjustment_highpass, gunangles); lowpass3(gunangles, frametime*cl_leanmodel_up_lowpass.value, frametime*cl_leanmodel_side_lowpass.value, 0, cl.gunangles_adjustment_lowpass, gunangles); // 5. use the adjusted vectors VectorAdd(vieworg, gunorg, gunorg); VectorAdd(viewangles, gunangles, gunangles); // bounded XY speed, used by several effects below xyspeed = bound (0, sqrt(clvelocity[0]*clvelocity[0] + clvelocity[1]*clvelocity[1]), 400); // vertical view bobbing code if (cl_bob.value && cl_bobcycle.value) { // LordHavoc: this code is *weird*, but not replacable (I think it // should be done in QC on the server, but oh well, quake is quake) // LordHavoc: figured out bobup: the time at which the sin is at 180 // degrees (which allows lengthening or squishing the peak or valley) cycle = cl.time / cl_bobcycle.value; cycle -= (int) cycle; if (cycle < cl_bobup.value) cycle = sin(M_PI * cycle / cl_bobup.value); else cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value)); // bob is proportional to velocity in the xy plane // (don't count Z, or jumping messes it up) bob = xyspeed * bound(0, cl_bob.value, 0.05); bob = bob*0.3 + bob*0.7*cycle; vieworg[2] += bob; // we also need to adjust gunorg, or this appears like pushing the gun! // In the old code, this was applied to vieworg BEFORE copying to gunorg, // but this is not viable with the new followmodel code as that would mean // that followmodel would work on the munged-by-bob vieworg and do feedback gunorg[2] += bob; } // horizontal view bobbing code if (cl_bob2.value && cl_bob2cycle.value) { vec3_t bob2vel; vec3_t forward, right, up; float side, front; cycle = cl.time / cl_bob2cycle.value; cycle -= (int) cycle; if (cycle < 0.5) cycle = cos(M_PI * cycle / 0.5); // cos looks better here with the other view bobbing using sin else cycle = cos(M_PI + M_PI * (cycle-0.5)/0.5); bob = bound(0, cl_bob2.value, 0.05) * cycle; // this value slowly decreases from 1 to 0 when we stop touching the ground. // The cycle is later multiplied with it so the view smooths back to normal if (clonground && !clcmdjump) // also block the effect while the jump button is pressed, to avoid twitches when bunny-hopping cl.bob2_smooth = 1; else { if(cl.bob2_smooth > 0) cl.bob2_smooth -= bound(0, cl_bob2smooth.value, 1); else cl.bob2_smooth = 0; } // calculate the front and side of the player between the X and Y axes AngleVectors(viewangles, forward, right, up); // now get the speed based on those angles. The bounds should match the same value as xyspeed's side = bound(-400, DotProduct (clvelocity, right) * cl.bob2_smooth, 400); front = bound(-400, DotProduct (clvelocity, forward) * cl.bob2_smooth, 400); VectorScale(forward, bob, forward); VectorScale(right, bob, right); // we use side with forward and front with right, so the bobbing goes // to the side when we walk forward and to the front when we strafe VectorMAMAM(side, forward, front, right, 0, up, bob2vel); vieworg[0] += bob2vel[0]; vieworg[1] += bob2vel[1]; // we also need to adjust gunorg, or this appears like pushing the gun! // In the old code, this was applied to vieworg BEFORE copying to gunorg, // but this is not viable with the new followmodel code as that would mean // that followmodel would work on the munged-by-bob vieworg and do feedback gunorg[0] += bob2vel[0]; gunorg[1] += bob2vel[1]; } // fall bobbing code // causes the view to swing down and back up when touching the ground if (cl_bobfall.value && cl_bobfallcycle.value) { if (!clonground) { cl.bobfall_speed = bound(-400, clvelocity[2], 0) * bound(0, cl_bobfall.value, 0.1); if (clvelocity[2] < -cl_bobfallminspeed.value) cl.bobfall_swing = 1; else cl.bobfall_swing = 0; // TODO really? } else { cl.bobfall_swing = max(0, cl.bobfall_swing - cl_bobfallcycle.value * frametime); bobfall = sin(M_PI * cl.bobfall_swing) * cl.bobfall_speed; vieworg[2] += bobfall; gunorg[2] += bobfall; } } // gun model bobbing code if (cl_bobmodel.value) { // calculate for swinging gun model // the gun bobs when running on the ground, but doesn't bob when you're in the air. // Sajt: I tried to smooth out the transitions between bob and no bob, which works // for the most part, but for some reason when you go through a message trigger or // pick up an item or anything like that it will momentarily jolt the gun. vec3_t forward, right, up; float bspeed; float s; float t; s = cl.time * cl_bobmodel_speed.value; if (clonground) { if (cl.time - cl.hitgroundtime < 0.2) { // just hit the ground, speed the bob back up over the next 0.2 seconds t = cl.time - cl.hitgroundtime; t = bound(0, t, 0.2); t *= 5; } else t = 1; } else { // recently left the ground, slow the bob down over the next 0.2 seconds t = cl.time - cl.lastongroundtime; t = 0.2 - bound(0, t, 0.2); t *= 5; } bspeed = xyspeed * 0.01f; AngleVectors (gunangles, forward, right, up); bob = bspeed * cl_bobmodel_side.value * cl_viewmodel_scale.value * sin (s) * t; VectorMA (gunorg, bob, right, gunorg); bob = bspeed * cl_bobmodel_up.value * cl_viewmodel_scale.value * cos (s * 2) * t; VectorMA (gunorg, bob, up, gunorg); } } } // calculate a view matrix for rendering the scene if (v_idlescale.value) { viewangles[0] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value; viewangles[1] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value; viewangles[2] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value; } Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, vieworg[0], vieworg[1], vieworg[2], viewangles[0], viewangles[1], viewangles[2], 1); // calculate a viewmodel matrix for use in view-attached entities Matrix4x4_Copy(&viewmodelmatrix_nobob, &r_refdef.view.matrix); Matrix4x4_ConcatScale(&viewmodelmatrix_nobob, cl_viewmodel_scale.value); Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix_withbob, gunorg[0], gunorg[1], gunorg[2], gunangles[0], gunangles[1], gunangles[2], cl_viewmodel_scale.value); VectorCopy(vieworg, cl.csqc_vieworiginfromengine); VectorCopy(viewangles, cl.csqc_viewanglesfromengine); Matrix4x4_Invert_Simple(&tmpmatrix, &r_refdef.view.matrix); Matrix4x4_Concat(&cl.csqc_viewmodelmatrixfromengine, &tmpmatrix, &viewmodelmatrix_withbob); } cl.calcrefdef_prevtime = cl.time; }
/* =========== SV_RunCmd =========== */ static void SV_RunCmd (usercmd_t *ucmd) { edict_t *ent; int i, n; int oldmsec; cmd = *ucmd; // chop up very long commands if (cmd.msec > 50) { oldmsec = ucmd->msec; cmd.msec = oldmsec/2; SV_RunCmd (&cmd); cmd.msec = oldmsec/2; cmd.impulse = 0; SV_RunCmd (&cmd); return; } if (!sv_player->v.fixangle) VectorCopy (ucmd->angles, sv_player->v.v_angle); sv_player->v.button0 = ucmd->buttons & 1; sv_player->v.button2 = (ucmd->buttons & 2)>>1; if (ucmd->buttons & 4 || sv_player->v.playerclass == CLASS_DWARF) // crouched? sv_player->v.flags2 = ((int)sv_player->v.flags2) | FL2_CROUCHED; else sv_player->v.flags2 = ((int)sv_player->v.flags2) & (~FL2_CROUCHED); if (ucmd->impulse) sv_player->v.impulse = ucmd->impulse; // // angles // show 1/3 the pitch angle and all the roll angle if (sv_player->v.health > 0) { if (!sv_player->v.fixangle) { sv_player->v.angles[PITCH] = -sv_player->v.v_angle[PITCH]/3; sv_player->v.angles[YAW] = sv_player->v.v_angle[YAW]; } sv_player->v.angles[ROLL] = V_CalcRoll (sv_player->v.angles, sv_player->v.velocity)*4; } host_frametime = ucmd->msec * 0.001; if (host_frametime > HX_FRAME_TIME) host_frametime = HX_FRAME_TIME; if (!host_client->spectator) { pr_global_struct->frametime = host_frametime; pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (pr_global_struct->PlayerPreThink); SV_RunThink (sv_player); } for (i = 0; i < 3; i++) pmove.origin[i] = sv_player->v.origin[i] + (sv_player->v.mins[i] - player_mins[i]); VectorCopy (sv_player->v.velocity, pmove.velocity); VectorCopy (sv_player->v.v_angle, pmove.angles); pmove.spectator = host_client->spectator; // pmove.waterjumptime = sv_player->v.teleport_time; pmove.numphysent = 1; pmove.physents[0].model = sv.worldmodel; pmove.cmd = *ucmd; pmove.dead = sv_player->v.health <= 0; pmove.oldbuttons = host_client->oldbuttons; pmove.hasted = sv_player->v.hasted; pmove.movetype = sv_player->v.movetype; pmove.crouched = (sv_player->v.hull == HULL_CROUCH); pmove.teleport_time = realtime + (sv_player->v.teleport_time - sv.time); // movevars.entgravity = host_client->entgravity; movevars.entgravity = sv_player->v.gravity; movevars.maxspeed = host_client->maxspeed; for (i = 0; i < 3; i++) { pmove_mins[i] = pmove.origin[i] - 256; pmove_maxs[i] = pmove.origin[i] + 256; } #if 1 AddLinksToPmove ( sv_areanodes ); #else AddAllEntsToPmove (); #endif #if 0 { int before, after; before = PM_TestPlayerPosition (pmove.origin); PlayerMove (); after = PM_TestPlayerPosition (pmove.origin); if (sv_player->v.health > 0 && before && !after ) Con_Printf ("player %s got stuck in playermove!!!!\n", host_client->name); } #else PlayerMove (); #endif host_client->oldbuttons = pmove.oldbuttons; // sv_player->v.teleport_time = pmove.waterjumptime; sv_player->v.waterlevel = waterlevel; sv_player->v.watertype = watertype; if (onground != -1) { sv_player->v.flags = (int)sv_player->v.flags | FL_ONGROUND; sv_player->v.groundentity = EDICT_TO_PROG(EDICT_NUM(pmove.physents[onground].info)); } else sv_player->v.flags = (int)sv_player->v.flags & ~FL_ONGROUND; for (i = 0; i < 3; i++) sv_player->v.origin[i] = pmove.origin[i] - (sv_player->v.mins[i] - player_mins[i]); #if 0 // truncate velocity the same way the net protocol will for (i = 0; i < 3; i++) sv_player->v.velocity[i] = (int)pmove.velocity[i]; #else VectorCopy (pmove.velocity, sv_player->v.velocity); #endif VectorCopy (pmove.angles, sv_player->v.v_angle); if (!host_client->spectator) { // link into place and touch triggers SV_LinkEdict (sv_player, true); // touch other objects for (i = 0; i < pmove.numtouch; i++) { n = pmove.physents[pmove.touchindex[i]].info; ent = EDICT_NUM(n); // Why not just do an SV_Impact here? // SV_Impact(sv_player,ent); if (sv_player->v.touch) { pr_global_struct->self = EDICT_TO_PROG(sv_player); pr_global_struct->other = EDICT_TO_PROG(ent); PR_ExecuteProgram (sv_player->v.touch); } if (!ent->v.touch || (playertouch[n/8]&(1<<(n%8)))) continue; pr_global_struct->self = EDICT_TO_PROG(ent); pr_global_struct->other = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (ent->v.touch); playertouch[n/8] |= 1 << (n%8); } } }