float sv_accelerate; float sv_friction; float sv_maxspeed; float sv_airaccelerate; float sv_maxairspeed; float sv_stopspeed; float sv_gravity; float sv_airaccel_sideways_friction; float sv_airaccel_qw; .float ladder_time; .entity ladder_entity; .float gravity; .float swamp_slowdown; .float lastflags; .float lastground; .float wasFlying; .float spectatorspeed; #define SHTEST_DELTA 10 #define SHTEST_THRESHOLD 1.1 .float shtest_next; .float shtest_accumulator; /* ============= PlayerJump When you press the jump key ============= */ void PlayerJump (void) { float mjumpheight; mjumpheight = cvar("sv_jumpvelocity"); if (self.waterlevel >= 2) { if (self.watertype == CONTENT_WATER) self.velocity_z = 200; else if (self.watertype == CONTENT_SLIME) self.velocity_z = 80; else self.velocity_z = 50; return; } if (!(self.flags & FL_ONGROUND)) return; if(!sv_pogostick) if (!(self.flags & FL_JUMPRELEASED)) return; if(g_runematch) { if(self.runes & RUNE_SPEED) { if(self.runes & CURSE_SLOW) mjumpheight = mjumpheight * cvar("g_balance_rune_speed_combo_jumpheight"); else mjumpheight = mjumpheight * cvar("g_balance_rune_speed_jumpheight"); } else if(self.runes & CURSE_SLOW) { mjumpheight = mjumpheight * cvar("g_balance_curse_slow_jumpheight"); } } if(g_minstagib && (self.items & IT_INVINCIBLE)) { mjumpheight = mjumpheight * cvar("g_minstagib_speed_jumpheight"); } self.velocity_z = self.velocity_z + mjumpheight; self.oldvelocity_z = self.velocity_z; self.flags = self.flags - FL_ONGROUND; self.flags = self.flags - FL_JUMPRELEASED; if (self.crouch) player_setanim(self.anim_duckjump, FALSE, TRUE, TRUE); else player_setanim(self.anim_jump, FALSE, TRUE, TRUE); if(g_jump_grunt) PlayerSound(playersound_jump, CHAN_PLAYER, VOICETYPE_PLAYERSOUND); } void CheckWaterJump() { local vector start, end; // check for a jump-out-of-water makevectors (self.angles); start = self.origin; start_z = start_z + 8; v_forward_z = 0; normalize(v_forward); end = start + v_forward*24; traceline (start, end, TRUE, self); if (trace_fraction < 1) { // solid at waist start_z = start_z + self.maxs_z - 8; end = start + v_forward*24; self.movedir = trace_plane_normal * -50; traceline (start, end, TRUE, self); if (trace_fraction == 1) { // open at eye level self.flags = self.flags | FL_WATERJUMP; self.velocity_z = 225; self.flags = self.flags - (self.flags & FL_JUMPRELEASED); self.teleport_time = time + 2; // safety net return; } } }; float racecar_angle(float forward, float down) { float ret, angle_mult; if(forward < 0) { forward = -forward; down = -down; } ret = vectoyaw('0 1 0' * down + '1 0 0' * forward); angle_mult = forward / (800 + forward); if(ret > 180) return ret * angle_mult + 360 * (1 - angle_mult); else return ret * angle_mult; } void RaceCarPhysics() { // using this move type for "big rigs" // the engine does not push the entity! float accel, steer, f; vector angles_save, rigvel; angles_save = self.angles; accel = bound(-1, self.movement_x / sv_maxspeed, 1); steer = bound(-1, self.movement_y / sv_maxspeed, 1); if(g_bugrigs_reverse_speeding) { if(accel < 0) { // back accel is DIGITAL // to prevent speedhack if(accel < -0.5) accel = -1; else accel = 0; } } self.angles_x = 0; self.angles_z = 0; makevectors(self.angles); // new forward direction! if(self.flags & FL_ONGROUND || g_bugrigs_air_steering) { float myspeed, upspeed, steerfactor, accelfactor; myspeed = self.velocity * v_forward; upspeed = self.velocity * v_up; // responsiveness factor for steering and acceleration f = 1 / (1 + pow(max(-myspeed, myspeed) / g_bugrigs_speed_ref, g_bugrigs_speed_pow)); //MAXIMA: f(v) := 1 / (1 + (v / g_bugrigs_speed_ref) ^ g_bugrigs_speed_pow); if(myspeed < 0 && g_bugrigs_reverse_spinning) steerfactor = -myspeed * g_bugrigs_steer; else steerfactor = -myspeed * f * g_bugrigs_steer; if(myspeed < 0 && g_bugrigs_reverse_speeding) accelfactor = g_bugrigs_accel; else accelfactor = f * g_bugrigs_accel; //MAXIMA: accel(v) := f(v) * g_bugrigs_accel; if(accel < 0) { if(myspeed > 0) { myspeed = max(0, myspeed - frametime * (g_bugrigs_friction_floor - g_bugrigs_friction_brake * accel)); } else { if(!g_bugrigs_reverse_speeding) myspeed = min(0, myspeed + frametime * g_bugrigs_friction_floor); } } else { if(myspeed >= 0) { myspeed = max(0, myspeed - frametime * g_bugrigs_friction_floor); } else { if(g_bugrigs_reverse_stopping) myspeed = 0; else myspeed = min(0, myspeed + frametime * (g_bugrigs_friction_floor + g_bugrigs_friction_brake * accel)); } } // terminal velocity = velocity at which 50 == accelfactor, that is, 1549 units/sec //MAXIMA: friction(v) := g_bugrigs_friction_floor; self.angles_y += steer * frametime * steerfactor; // apply steering makevectors(self.angles); // new forward direction! myspeed += accel * accelfactor * frametime; rigvel = myspeed * v_forward + '0 0 1' * upspeed; } else { myspeed = vlen(self.velocity); // responsiveness factor for steering and acceleration f = 1 / (1 + pow(max(0, myspeed / g_bugrigs_speed_ref), g_bugrigs_speed_pow)); steerfactor = -myspeed * f; self.angles_y += steer * frametime * steerfactor; // apply steering rigvel = self.velocity; makevectors(self.angles); // new forward direction! } rigvel = rigvel * max(0, 1 - vlen(rigvel) * g_bugrigs_friction_air * frametime); //MAXIMA: airfriction(v) := v * v * g_bugrigs_friction_air; //MAXIMA: total_acceleration(v) := accel(v) - friction(v) - airfriction(v); //MAXIMA: solve(total_acceleration(v) = 0, v); if(g_bugrigs_planar_movement) { vector rigvel_xy, neworigin, up; float mt; rigvel_z -= frametime * sv_gravity; // 4x gravity plays better rigvel_xy = rigvel; rigvel_xy_z = 0; if(g_bugrigs_planar_movement_car_jumping && !g_touchexplode) // touchexplode is a better way to handle collisions mt = MOVE_NORMAL; else mt = MOVE_NOMONSTERS; tracebox(self.origin, self.mins, self.maxs, self.origin + '0 0 1024', mt, self); up = trace_endpos - self.origin; // BUG RIGS: align the move to the surface instead of doing collision testing // can we move? tracebox(trace_endpos, self.mins, self.maxs, trace_endpos + rigvel_xy * frametime, mt, self); // align to surface tracebox(trace_endpos, self.mins, self.maxs, trace_endpos - up + '0 0 1' * rigvel_z * frametime, mt, self); if(trace_fraction < 0.5) { trace_fraction = 1; neworigin = self.origin; } else neworigin = trace_endpos; if(trace_fraction < 1) { // now set angles_x so that the car points parallel to the surface self.angles = vectoangles( '1 0 0' * v_forward_x * trace_plane_normal_z + '0 1 0' * v_forward_y * trace_plane_normal_z + '0 0 1' * -(v_forward_x * trace_plane_normal_x + v_forward_y * trace_plane_normal_y) ); self.flags |= FL_ONGROUND; } else { // now set angles_x so that the car points forward, but is tilted in velocity direction self.flags &~= FL_ONGROUND; } self.velocity = (neworigin - self.origin) * (1.0 / frametime); self.movetype = MOVETYPE_NOCLIP; } else { rigvel_z -= frametime * sv_gravity; // 4x gravity plays better self.velocity = rigvel; self.movetype = MOVETYPE_FLY; } trace_fraction = 1; tracebox(self.origin, self.mins, self.maxs, self.origin - '0 0 4', MOVE_NORMAL, self); if(trace_fraction != 1) { self.angles = vectoangles2( '1 0 0' * v_forward_x * trace_plane_normal_z + '0 1 0' * v_forward_y * trace_plane_normal_z + '0 0 1' * -(v_forward_x * trace_plane_normal_x + v_forward_y * trace_plane_normal_y), trace_plane_normal ); } else { vector vel_local; vel_local_x = v_forward * self.velocity; vel_local_y = v_right * self.velocity; vel_local_z = v_up * self.velocity; self.angles_x = racecar_angle(vel_local_x, vel_local_z); self.angles_z = racecar_angle(-vel_local_y, vel_local_z); } // smooth the angles vector vf1, vu1, smoothangles; makevectors(self.angles); f = bound(0, frametime * g_bugrigs_angle_smoothing, 1); if(f == 0) f = 1; vf1 = v_forward * f; vu1 = v_up * f; makevectors(angles_save); vf1 = vf1 + v_forward * (1 - f); vu1 = vu1 + v_up * (1 - f); smoothangles = vectoangles2(vf1, vu1); self.angles_x = -smoothangles_x; self.angles_z = smoothangles_z; } .vector movement_old; .float buttons_old; .vector v_angle_old; void Nixnex_GiveCurrentWeapon(); void SV_PlayerPhysics() { local vector wishvel, wishdir, v; local float wishspeed, f, maxspd_mod, spd, maxairspd, airaccel, swampspd_mod, shtest_score, buttons; string temps; buttons = self.BUTTON_ATCK + 2 * self.BUTTON_JUMP + 4 * self.BUTTON_ATCK2 + 8 * self.BUTTON_ZOOM + 16 * self.BUTTON_CROUCH + 32 * self.BUTTON_HOOK + 64 * self.BUTTON_USE; if(!sv_maxidle_spectatorsareidle || self.movetype == MOVETYPE_WALK) { if(buttons != self.buttons_old || self.movement != self.movement_old || self.v_angle != self.v_angle_old) self.parm_idlesince = time; } self.buttons_old = buttons; self.movement_old = self.movement; self.v_angle_old = self.v_angle; if(time > self.shtest_next) { if(self.shtest_next > 0) { // self.shtest_accumulator: // started at time - SHTEST_DELTA // should be at SHTEST_DELTA shtest_score = self.shtest_accumulator / (SHTEST_DELTA + time - self.shtest_next); if(shtest_score > SHTEST_THRESHOLD) print("TIME PARADOX: shtest for ", self.netname, " said ", ftos(shtest_score), "\n"); else if(cvar("developer_shtest")) dprint("okay: shtest for ", self.netname, " said ", ftos(shtest_score), "\n"); } self.shtest_next = time + SHTEST_DELTA; self.shtest_accumulator = 0; } self.shtest_accumulator += frametime; if (clienttype(self) == CLIENTTYPE_BOT) bot_think(); if (self.movetype == MOVETYPE_NONE && self.disableclientprediction != 2) return; if (self.punchangle != '0 0 0') { f = vlen(self.punchangle) - 10 * frametime; if (f > 0) self.punchangle = normalize(self.punchangle) * f; else self.punchangle = '0 0 0'; } if (self.punchvector != '0 0 0') { f = vlen(self.punchvector) - 30 * frametime; if (f > 0) self.punchvector = normalize(self.punchvector) * f; else self.punchvector = '0 0 0'; } maxspd_mod = 1; if(g_runematch) { if(self.runes & RUNE_SPEED) { if(self.runes & CURSE_SLOW) maxspd_mod = maxspd_mod * cvar("g_balance_rune_speed_combo_moverate"); else maxspd_mod = maxspd_mod * cvar("g_balance_rune_speed_moverate"); } else if(self.runes & CURSE_SLOW) { maxspd_mod = maxspd_mod * cvar("g_balance_curse_slow_moverate"); } } if(g_minstagib && (self.items & IT_INVINCIBLE)) { maxspd_mod = cvar("g_minstagib_speed_moverate"); } swampspd_mod = 1; if(self.in_swamp) { swampspd_mod = self.swamp_slowdown; //cvar("g_balance_swamp_moverate"); } if(self.flags & FL_NOTARGET) { maxspd_mod = cvar("sv_spectator_speed_multiplier"); if(!self.spectatorspeed) self.spectatorspeed = maxspd_mod; if(self.impulse && self.impulse <= 19) { if(self.lastflags & FL_NOTARGET) { if(self.impulse == 10 || self.impulse == 15 || self.impulse == 18) self.spectatorspeed = bound(1, self.spectatorspeed + 0.5, 5); else if(self.impulse == 11) self.spectatorspeed = maxspd_mod; else if(self.impulse == 12 || self.impulse == 16 || self.impulse == 19) self.spectatorspeed = bound(1, self.spectatorspeed - 0.5, 5); else if(self.impulse >= 1 && self.impulse <= 9) self.spectatorspeed = 1 + 0.5 * (self.impulse - 1); } // otherwise just clear self.impulse = 0; } maxspd_mod = self.spectatorspeed; } spd = sv_maxspeed * maxspd_mod * swampspd_mod; if(self.speed != spd) { self.speed = spd; temps = ftos(spd); stuffcmd(self, strcat("cl_forwardspeed ", temps, "\n")); stuffcmd(self, strcat("cl_backspeed ", temps, "\n")); stuffcmd(self, strcat("cl_sidespeed ", temps, "\n")); stuffcmd(self, strcat("cl_upspeed ", temps, "\n")); temps = ftos(sv_accelerate * maxspd_mod); stuffcmd(self, strcat("cl_movement_accelerate ", temps, "\n")); } // if dead, behave differently if (self.deadflag) return; if (!self.fixangle && !g_bugrigs) { self.angles_x = 0; self.angles_y = self.v_angle_y; self.angles_z = 0; } if(self.flags & FL_ONGROUND) if(self.wasFlying) { self.wasFlying = 0; if(self.waterlevel < 2) if(time >= self.ladder_time) if not(self.hook) { self.nextstep = time + 0.3 + random() * 0.1; trace_dphitq3surfaceflags = 0; tracebox(self.origin, self.mins, self.maxs, self.origin - '0 0 1', MOVE_NOMONSTERS, self); if not(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOSTEPS) { if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS) GlobalSound(globalsound_metalfall, CHAN_PLAYER, VOICETYPE_PLAYERSOUND); else GlobalSound(globalsound_fall, CHAN_PLAYER, VOICETYPE_PLAYERSOUND); } } } if(IsFlying(self)) self.wasFlying = 1; if(self.classname == "player") { if(sv_doublejump) { self.flags = self.flags - (self.flags & FL_ONGROUND); tracebox(self.origin + '0 0 1', self.mins, self.maxs, self.origin - '0 0 2', MOVE_NORMAL, self); if(trace_fraction < 1 && trace_plane_normal_z > 0.7) self.flags = self.flags | FL_ONGROUND; } if (self.BUTTON_JUMP) PlayerJump (); else self.flags = self.flags | FL_JUMPRELEASED; if (self.waterlevel == 2) CheckWaterJump (); } if (self.flags & FL_WATERJUMP ) { self.velocity_x = self.movedir_x; self.velocity_y = self.movedir_y; if (time > self.teleport_time || self.waterlevel == 0) { self.flags = self.flags - (self.flags & FL_WATERJUMP); self.teleport_time = 0; } } else if (g_bugrigs && self.classname == "player") { RaceCarPhysics(); } else if (self.movetype == MOVETYPE_NOCLIP || self.movetype == MOVETYPE_FLY) { // noclipping or flying self.flags = self.flags - (self.flags & FL_ONGROUND); self.velocity = self.velocity * (1 - frametime * sv_friction); makevectors(self.v_angle); //wishvel = v_forward * self.movement_x + v_right * self.movement_y + v_up * self.movement_z; wishvel = v_forward * self.movement_x + v_right * self.movement_y + '0 0 1' * self.movement_z; // acceleration wishdir = normalize(wishvel); wishspeed = vlen(wishvel); if (wishspeed > sv_maxspeed*maxspd_mod) wishspeed = sv_maxspeed*maxspd_mod; if (time >= self.teleport_time) { f = wishspeed - (self.velocity * wishdir); if (f > 0) self.velocity = self.velocity + wishdir * min(f, sv_accelerate*maxspd_mod * frametime * wishspeed); } } else if (self.waterlevel >= 2) { // swimming self.flags = self.flags - (self.flags & FL_ONGROUND); makevectors(self.v_angle); //wishvel = v_forward * self.movement_x + v_right * self.movement_y + v_up * self.movement_z; wishvel = v_forward * self.movement_x + v_right * self.movement_y + '0 0 1' * self.movement_z; if (wishvel == '0 0 0') wishvel = '0 0 -60'; // drift towards bottom wishdir = normalize(wishvel); wishspeed = vlen(wishvel); if (wishspeed > sv_maxspeed*maxspd_mod) wishspeed = sv_maxspeed*maxspd_mod; wishspeed = wishspeed * 0.7; // water friction self.velocity = self.velocity * (1 - frametime * sv_friction); // water acceleration f = wishspeed - (self.velocity * wishdir); if (f > 0) self.velocity = self.velocity + wishdir * min(f, sv_accelerate*maxspd_mod * frametime * wishspeed); } else if (time < self.ladder_time) { // on a spawnfunc_func_ladder or swimming in spawnfunc_func_water self.flags = self.flags - (self.flags & FL_ONGROUND); self.velocity = self.velocity * (1 - frametime * sv_friction); makevectors(self.v_angle); //wishvel = v_forward * self.movement_x + v_right * self.movement_y + v_up * self.movement_z; wishvel = v_forward * self.movement_x + v_right * self.movement_y + '0 0 1' * self.movement_z; if (self.gravity) self.velocity_z = self.velocity_z + self.gravity * sv_gravity * frametime; else self.velocity_z = self.velocity_z + sv_gravity * frametime; if (self.ladder_entity.classname == "func_water") { f = vlen(wishvel); if (f > self.ladder_entity.speed) wishvel = wishvel * (self.ladder_entity.speed / f); self.watertype = self.ladder_entity.skin; f = self.ladder_entity.origin_z + self.ladder_entity.maxs_z; if ((self.origin_z + self.view_ofs_z) < f) self.waterlevel = 3; else if ((self.origin_z + (self.mins_z + self.maxs_z) * 0.5) < f) self.waterlevel = 2; else if ((self.origin_z + self.mins_z + 1) < f) self.waterlevel = 1; else { self.waterlevel = 0; self.watertype = CONTENT_EMPTY; } } // acceleration wishdir = normalize(wishvel); wishspeed = vlen(wishvel); if (wishspeed > sv_maxspeed) wishspeed = sv_maxspeed; if (time >= self.teleport_time) { f = wishspeed - (self.velocity * wishdir); if (f > 0) self.velocity = self.velocity + wishdir * min(f, sv_accelerate*maxspd_mod * frametime * wishspeed); } } else if (self.flags & FL_ONGROUND) { // walking makevectors(self.v_angle_y * '0 1 0'); wishvel = v_forward * self.movement_x + v_right * self.movement_y; if(!(self.lastflags & FL_ONGROUND)) { if(cvar("speedmeter")) dprint(strcat("landing velocity: ", vtos(self.velocity), " (abs: ", ftos(vlen(self.velocity)), ")\n")); if(self.lastground < time - 0.3) self.velocity = self.velocity * (1 - cvar("sv_friction_on_land")); if(self.jumppadcount > 1) dprint(strcat(ftos(self.jumppadcount), "x jumppad combo\n")); self.jumppadcount = 0; } if (self.velocity_x || self.velocity_y) if (!(self.flags & FL_JUMPRELEASED) || !self.BUTTON_JUMP) { v = self.velocity; v_z = 0; f = vlen(v); if (f < sv_stopspeed) f = 1 - frametime * (sv_stopspeed / f) * sv_friction; else f = 1 - frametime * sv_friction; if (f > 0) self.velocity = self.velocity * f; else self.velocity = '0 0 0'; } // acceleration wishdir = normalize(wishvel); wishspeed = vlen(wishvel); if (wishspeed > sv_maxspeed*maxspd_mod) wishspeed = sv_maxspeed*maxspd_mod; if (self.crouch) wishspeed = wishspeed * 0.5; if (time >= self.teleport_time) { f = wishspeed - (self.velocity * wishdir); if (f > 0) self.velocity = self.velocity + wishdir * min(f, sv_accelerate*maxspd_mod * frametime * wishspeed); } } else { if(maxspd_mod < 1) { maxairspd = sv_maxairspeed*maxspd_mod; airaccel = sv_airaccelerate*maxspd_mod; } else { maxairspd = sv_maxairspeed; airaccel = sv_airaccelerate; } // airborn makevectors(self.v_angle_y * '0 1 0'); wishvel = v_forward * self.movement_x + v_right * self.movement_y; // acceleration wishdir = normalize(wishvel); wishspeed = vlen(wishvel); if (wishspeed > maxairspd) wishspeed = maxairspd; if (self.crouch) wishspeed = wishspeed * 0.5; if (time >= self.teleport_time) { // NOTE: this does the same as the commented out old code if: // sv_airaccel_qw 0 // sv_airaccel_sideways_friction 0 float vel_straight; float vel_z; vector vel_perpend; vel_straight = self.velocity * wishdir; vel_z = self.velocity_z; vel_perpend = self.velocity - vel_straight * wishdir - vel_z * '0 0 1'; f = wishspeed - vel_straight; if(f > 0) vel_straight = vel_straight + min(f, airaccel * frametime * wishspeed) * sv_airaccel_qw; if(wishspeed > 0) vel_straight = vel_straight + min(wishspeed, airaccel * frametime * wishspeed) * (1 - sv_airaccel_qw); // anti-sideways friction to fix QW-style bunnyhopping vel_perpend = vel_perpend * (1 - frametime * (wishspeed / maxairspd) * sv_airaccel_sideways_friction); self.velocity = vel_straight * wishdir + vel_z * '0 0 1' + vel_perpend; /* f = wishspeed;// - (self.velocity * wishdir); if (f > 0) self.velocity = self.velocity + wishdir * min(f, airaccel * frametime * wishspeed); */ } } if(self.flags & FL_ONGROUND) self.lastground = time; self.lastflags = self.flags; };