/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "../game/q_shared.h" #include "gs_public.h" //=============================================================== // WARSOW player AAboxes sizes vec3_t playerbox_stand_mins = { -16, -16, -24 }; vec3_t playerbox_stand_maxs = { 16, 16, 40 }; int playerbox_stand_viewheight = 32; vec3_t playerbox_crouch_mins = { -16, -16, -24 }; vec3_t playerbox_crouch_maxs = { 16, 16, 16 }; int playerbox_crouch_viewheight = 12; vec3_t playerbox_gib_mins = { -16, -16, 0 }; vec3_t playerbox_gib_maxs = { 16, 16, 16 }; int playerbox_gib_viewheight = 8; // original qfusion sizes //#define PLAYERBOX_STAND_MINS { -15, -15, -24 } //#define PLAYERBOX_STAND_MAXS { 15, 15, 32 } //#define PLAYERBOX_STAND_VIEWHEIGHT 26 //#define PLAYERBOX_CROUCH_MINS { -15, -15, -24 } //#define PLAYERBOX_CROUCH_MAXS { 15, 15, 14 } //#define PLAYERBOX_CROUCH_VIEWHEIGHT 12 //#define PLAYERBOX_GIB_MINS { -15, -15, 0 } //#define PLAYERBOX_GIB_MAXS { 15, 15, 16 } //#define PLAYERBOX_GIB_VIEWHEIGHT 8 #define PM_WALLJUMP_TIMEDELAY 1200 //=============================================================== // all of the locals will be zeroed before each // pmove, just to make damn sure we don't have // any differences when running on client or server typedef struct { vec3_t origin; // full float precision vec3_t velocity; // full float precision vec3_t forward, right, up; float frametime; int groundsurfFlags; cplane_t groundplane; int groundcontents; vec3_t previous_origin; qboolean ladder; } pml_t; pmove_t *pm; pml_t pml; // movement parameters float pm_stopspeed = 100; float pm_maxspeed = 320; float pm_maxwalkspeed = 160; float pm_duckspeed = 100; float pm_accelerate = 15; // Kurim : initially 10 float pm_wateraccelerate = 10; // initially 6 float pm_friction = 8; // initially 6 float pm_waterfriction = 1; // wsw physics // cpm-like float cpm_pm_airstopaccelerate = 2.5; float cpm_pm_aircontrol = 150; float cpm_pm_strafeaccelerate = 70; float cpm_pm_wishspeed = 30; // jump float cpm_pm_jump_z = 100; float pm_jump_velocity = 280; // wsw float dash_height = 170; float dash_ups = 450; float wj_height = 300; // looks to be 200 in wsw qfr5 float wj_angle = 45.0; float wj_cooldown = 700; int pm_dash_time = 1000; // delay in milliseconds // // Kurim : some functions/defines that can be useful to work on the horizontal mouvement of player : // #define VectorScale2D(in,scale,out) ((out)[0]=(in)[0]*(scale),(out)[1]=(in)[1]*(scale)) #define DotProduct2D(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]) static vec_t VectorNormalize2D( vec3_t v ) // ByMiK : normalize horizontally (don't affect Z value) { float length, ilength; length = v[0]*v[0] + v[1]*v[1]; if( length ) { length = sqrt( length ); // FIXME ilength = 1.0f/length; v[0] *= ilength; v[1] *= ilength; } return length; } // Could be used to test if player walk touching a wall, if not used in any other part of pm code i'll integrate // this function to the walljumpcheck function. // usage : nbTestDir = nb of direction to test around the player // maxZnormal is the Z value of the normal of a poly to considere it as a wall // normal is a pointer to the normal of the nearest wall static void PlayerTouchWall( int nbTestDir, float maxZnormal, vec3_t* normal ) { vec3_t min, max, dir; int i, j; trace_t trace; float dist = 1.0; for( i = 0; i < nbTestDir; i++ ) { dir[0] = pml.origin[0] + (pm->maxs[0]*cos((360/nbTestDir)*i)); // cos( (360/Imax) * i ) dir[1] = pml.origin[1] + (pm->maxs[1]*sin((360/nbTestDir)*i)); dir[2] = pml.origin[2]; for( j = 0; j < 2; j++ ) { min[j] = pm->mins[j]; max[j] = pm->maxs[j]; } min[2] = max[2] = 0; VectorScale( dir, 1.002, dir ); pm->trace( &trace, pml.origin, min, max, dir ); if( trace.allsolid ) return; if( trace.fraction == 1 ) continue; // no wall in this direction if( trace.fraction > 0 ) { if( dist > trace.fraction && abs(trace.plane.normal[2]) < maxZnormal ) { dist = trace.fraction; VectorCopy( trace.plane.normal, *normal ); } } } } /* walking up a step should kill some velocity */ /* ================== PM_ClipVelocity -- Kurim : modified for wsw Slide off of the impacting object returns the blocked flags (1 = floor, 2 = step / wall) ================== */ #define STOP_EPSILON 0.1 static void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) { float backoff; float change; int i; backoff = DotProduct( in, normal ); if( backoff < 0 ) { backoff *= overbounce; } else { backoff /= overbounce; } for( i = 0; i < 3; i++ ) { change = normal[i]*backoff; out[i] = in[i] - change; } } /* ================== PM_SlideMove Returns a new origin, velocity, and contact entity Does not modify any world state? ================== */ #define MIN_STEP_NORMAL 0.7 // can't step up onto very steep slopes #define MAX_CLIP_PLANES 5 static int PM_SlideMove( void ) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t planes[MAX_CLIP_PLANES]; vec3_t primal_velocity; int i, j; trace_t trace; vec3_t end; float time_left; int blocked = 0; numbumps = 4; VectorCopy( pml.velocity, primal_velocity ); numplanes = 0; time_left = pml.frametime; for( bumpcount = 0; bumpcount < numbumps; bumpcount++ ) { VectorMA( pml.origin, time_left, pml.velocity, end ); pm->trace( &trace, pml.origin, pm->mins, pm->maxs, end ); if( trace.allsolid ) { // entity is trapped in another solid pml.velocity[2] = 0; // don't build up falling damage return 3; } if( trace.fraction > 0 ) { // actually covered some distance VectorCopy( trace.endpos, pml.origin ); numplanes = 0; } if( trace.fraction == 1 ) break; // moved the entire distance // save entity for contact if( pm->numtouch < MAXTOUCH ) { pm->touchents[pm->numtouch] = trace.ent; pm->numtouch++; } if( !trace.plane.normal[2] ) { blocked |= 2; // step } time_left -= time_left * trace.fraction; // slide along this plane if( numplanes >= MAX_CLIP_PLANES ) { // this shouldn't really happen VectorClear( pml.velocity ); break; } VectorCopy( trace.plane.normal, planes[numplanes] ); numplanes++; // // modify original_velocity so it parallels all of the clip planes // for( i = 0; i < numplanes; i++ ) { PM_ClipVelocity( pml.velocity, planes[i], pml.velocity, 1.01f ); for( j = 0; j < numplanes ; j++ ) if( j != i ) { if( DotProduct(pml.velocity, planes[j]) < 0 ) break; // not ok } if( j == numplanes ) break; } if( i != numplanes ) { // go along this plane } else { // go along the crease if( numplanes != 2 ) { VectorClear( pml.velocity ); break; } CrossProduct( planes[0], planes[1], dir ); VectorNormalize( dir ); d = DotProduct( dir, pml.velocity ); VectorScale( dir, d, pml.velocity ); } // // if velocity is against the original velocity, stop dead // to avoid tiny occilations in sloping corners // if( DotProduct(pml.velocity, primal_velocity) <= 0 ) { VectorClear( pml.velocity ); break; } } if( pm->s.pm_time ) { VectorCopy( primal_velocity, pml.velocity ); } return blocked; } /* ================== PM_StepSlideMove Each intersection will try to step over the obstruction instead of sliding along it. ================== */ static void PM_StepSlideMove( void ) { vec3_t start_o, start_v; vec3_t down_o, down_v; trace_t trace; float down_dist, up_dist; vec3_t up, down; int blocked; VectorCopy( pml.origin, start_o ); VectorCopy( pml.velocity, start_v ); blocked = PM_SlideMove(); VectorCopy( pml.origin, down_o ); VectorCopy( pml.velocity, down_v ); VectorCopy( start_o, up ); up[2] += STEPSIZE; pm->trace( &trace, up, pm->mins, pm->maxs, up ); if( trace.allsolid ) return; // can't step up // try sliding above VectorCopy( up, pml.origin ); VectorCopy( start_v, pml.velocity ); PM_SlideMove(); // push down the final amount VectorCopy( pml.origin, down ); down[2] -= STEPSIZE; pm->trace( &trace, pml.origin, pm->mins, pm->maxs, down ); if( !trace.allsolid ) { VectorCopy( trace.endpos, pml.origin ); } VectorCopy( pml.origin, up ); // decide which one went farther down_dist = (down_o[0] - start_o[0])*(down_o[0] - start_o[0]) + (down_o[1] - start_o[1])*(down_o[1] - start_o[1]); up_dist = (up[0] - start_o[0])*(up[0] - start_o[0]) + (up[1] - start_o[1])*(up[1] - start_o[1]); if( down_dist >= up_dist || trace.allsolid || (trace.fraction != 1.0 && trace.plane.normal[2] < MIN_STEP_NORMAL) ) { VectorCopy( down_o, pml.origin ); VectorCopy( down_v, pml.velocity ); return; } if( (blocked & 2) || trace.plane.normal[2] == 1 ) { int stepsize = (int)( pml.origin[2] - (pml.previous_origin[2]*(1.0/16.0)) ); if( stepsize > 0 ) pm->step = stepsize ; } //!! Special case // if we were walking along a plane, then we need to copy the Z over pml.velocity[2] = down_v[2]; } /* ================== PM_Friction -- Modified for wsw Handles both ground friction and water friction ================== */ static void PM_Friction( void ) { float *vel; float speed, newspeed, control; float friction; float drop; vel = pml.velocity; speed = vel[0]*vel[0] +vel[1]*vel[1] + vel[2]*vel[2]; if( speed < 1 ) { vel[0] = 0; vel[1] = 0; return; } speed = sqrt( speed ); drop = 0; // apply ground friction if( ( ( ( (pm->groundentity != -1) && !(pml.groundsurfFlags & SURF_SLICK) ) ) && (pm->waterlevel < 2) ) || (pml.ladder) ) { if( pm->s.stats[PM_STAT_KNOCKBACK] <= 0 ) { friction = pm_friction; control = speed < pm_stopspeed ? pm_stopspeed : speed; drop += control*friction*pml.frametime; } } // apply water friction if( (pm->waterlevel >= 2) && !pml.ladder ) drop += speed*pm_waterfriction*pm->waterlevel*pml.frametime; // scale the velocity newspeed = speed - drop; if( newspeed <= 0 ) { newspeed = 0; VectorClear( vel ); } else { newspeed /= speed; VectorScale( vel, newspeed, vel ); } } /* ============== PM_Accelerate Handles user intended acceleration ============== */ static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel ) { int i; float addspeed, accelspeed, currentspeed; currentspeed = DotProduct( pml.velocity, wishdir ); addspeed = wishspeed - currentspeed; if( addspeed <= 0 ) return; accelspeed = accel*pml.frametime*wishspeed; if( accelspeed > addspeed ) accelspeed = addspeed; for( i = 0; i < 3; i++ ) pml.velocity[i] += accelspeed*wishdir[i]; } static void CPM_PM_Aircontrol( pmove_t *pm, vec3_t wishdir, float wishspeed ) { float zspeed, speed, dot, k; int i; float fmove,smove; // accelerate fmove = pm->cmd.forwardmove; smove = pm->cmd.sidemove; if( (smove > 0 || smove < 0) || (wishspeed == 0.0) ) return; // can't control movement if not moveing forward or backward zspeed = pml.velocity[2]; pml.velocity[2] = 0; speed = VectorNormalize( pml.velocity ); dot = DotProduct( pml.velocity,wishdir ); k = 32; k *= cpm_pm_aircontrol*dot*dot*pml.frametime; if( dot > 0 ) { // we can't change direction while slowing down for (i=0; i < 2; i++) pml.velocity[i] = pml.velocity[i]*speed + wishdir[i]*k; VectorNormalize( pml.velocity ); } for( i = 0; i < 2; i++ ) pml.velocity[i] *=speed; pml.velocity[2] = zspeed; } #if 0 // never used static void PM_AirAccelerate( vec3_t wishdir, float wishspeed, float accel ) { int i; float addspeed, accelspeed, currentspeed, wishspd = wishspeed; if( wishspd > 30 ) wishspd = 30; currentspeed = DotProduct( pml.velocity, wishdir ); addspeed = wishspd - currentspeed; if( addspeed <= 0 ) return; accelspeed = accel * wishspeed * pml.frametime; if( accelspeed > addspeed ) accelspeed = addspeed; for( i = 0; i < 3; i++ ) pml.velocity[i] += accelspeed*wishdir[i]; } #endif /* ============= PM_AddCurrents ============= */ static void PM_AddCurrents( vec3_t wishvel ) { // // account for ladders // if( pml.ladder && fabs(pml.velocity[2]) <= 200 ) { if( (pm->viewangles[PITCH] <= -15) && (pm->cmd.forwardmove > 0) ) wishvel[2] = 200; else if( (pm->viewangles[PITCH] >= 15) && (pm->cmd.forwardmove > 0) ) wishvel[2] = -200; else if( pm->cmd.upmove > 0 ) wishvel[2] = 200; else if( pm->cmd.upmove < 0 ) wishvel[2] = -200; else wishvel[2] = 0; // limit horizontal speed when on a ladder clamp( wishvel[0], -25, 25 ); clamp( wishvel[1], -25, 25 ); } } /* =================== PM_WaterMove =================== */ static void PM_WaterMove( void ) { int i; vec3_t wishvel; float wishspeed; vec3_t wishdir; // user intentions for( i = 0; i < 3; i++ ) wishvel[i] = pml.forward[i]*pm->cmd.forwardmove + pml.right[i]*pm->cmd.sidemove; if( !pm->cmd.forwardmove && !pm->cmd.sidemove && !pm->cmd.upmove ) wishvel[2] -= 60; // drift towards bottom else wishvel[2] += pm->cmd.upmove; PM_AddCurrents( wishvel ); VectorCopy( wishvel, wishdir ); wishspeed = VectorNormalize( wishdir ); if( wishspeed > pm_maxspeed ) { wishspeed = pm_maxspeed/wishspeed; VectorScale( wishvel, wishspeed, wishvel ); wishspeed = pm_maxspeed; } wishspeed *= 0.5; PM_Accelerate( wishdir, wishspeed, pm_wateraccelerate ); PM_StepSlideMove(); } /* =================== PM_AirMove -- Kurim : modified for wsw =================== */ static void PM_AirMove( void ) { int i; vec3_t wishvel; float fmove, smove; vec3_t wishdir; float wishspeed; float maxspeed; float accel; float wishspeed2; fmove = pm->cmd.forwardmove; smove = pm->cmd.sidemove; for( i = 0; i < 2; i++ ) wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; wishvel[2] = 0; PM_AddCurrents( wishvel ); VectorCopy( wishvel, wishdir ); wishspeed = VectorNormalize( wishdir ); // clamp to server defined max speed // wsw : jal : fixme?: shall we use a pmflag? if( pm->s.pm_flags & PMF_DUCKED ) { maxspeed = pm_duckspeed; } else if( pm->cmd.buttons & BUTTON_WALK ) { maxspeed = pm_maxwalkspeed; } else maxspeed = pm_maxspeed; if( wishspeed > maxspeed ) { wishspeed = maxspeed/wishspeed; VectorScale( wishvel, wishspeed, wishvel ); wishspeed = maxspeed; } if( pml.ladder ) { PM_Accelerate( wishdir, wishspeed, pm_accelerate ); if( !wishvel[2] ) { if( pml.velocity[2] > 0 ) { pml.velocity[2] -= pm->s.gravity * pml.frametime; if( pml.velocity[2] < 0 ) pml.velocity[2] = 0; } else { pml.velocity[2] += pm->s.gravity * pml.frametime; if( pml.velocity[2] > 0 ) pml.velocity[2] = 0; } } PM_StepSlideMove(); } else if( pm->groundentity != -1 ) { // walking on ground if( pml.velocity[2] > 0 ) pml.velocity[2] = 0; //!!! this is before the accel PM_Accelerate( wishdir, wishspeed, pm_accelerate ); // fix for negative trigger_gravity fields if( pm->s.gravity > 0 ) { if( pml.velocity[2] > 0 ) pml.velocity[2] = 0; } else pml.velocity[2] -= pm->s.gravity * pml.frametime; if( !pml.velocity[0] && !pml.velocity[1] ) return; PM_StepSlideMove(); } else // Kurim : changing : THIS : /* { // not on ground, so little effect on velocity if (pm_airaccelerate) PM_AirAccelerate (wishdir, wishspeed, pm_accelerate); else PM_Accelerate (wishdir, wishspeed, 1); // add gravity pml.velocity[2] -= pm->s.gravity * pml.frametime; PM_StepSlideMove (); } */ // TO THIS : { // Air Control wishspeed2 = wishspeed; if( DotProduct(pml.velocity, wishdir) < 0 && !(pm->s.pm_flags & PMF_WALLJUMPING) ) accel = cpm_pm_airstopaccelerate; else accel = 1; //instead of pm_airaccelerate, (correcting the forward bug); if( pm->s.pm_flags & PMF_WALLJUMPING ) accel = 0; // no stopmove while walljumping if( (smove > 0 || smove < 0) && fmove == 0 ) { if( wishspeed > cpm_pm_wishspeed ) wishspeed = cpm_pm_wishspeed; accel = cpm_pm_strafeaccelerate; } // Air control PM_Accelerate( wishdir, wishspeed, accel ); if( cpm_pm_aircontrol && !(pm->s.pm_flags & PMF_WALLJUMPING) ) // no air ctrl while wjing CPM_PM_Aircontrol( pm, wishdir, wishspeed2 ); // add gravity pml.velocity[2] -= pm->s.gravity * pml.frametime; PM_StepSlideMove(); } } /* ============= PM_CategorizePosition ============= */ #define JITSPOES_CLIPBUGFIX // jal : it bugs more than fixes? static void PM_CategorizePosition( void ) { vec3_t point; int cont; trace_t trace; int sample1; int sample2; // if the player hull point one unit down is solid, the player // is on ground // see if standing on something solid point[0] = pml.origin[0]; point[1] = pml.origin[1]; point[2] = pml.origin[2] - 0.25; if( pml.velocity[2] > 180 ) // !!ZOID changed from 100 to 180 (ramp accel) { pm->s.pm_flags &= ~PMF_ON_GROUND; pm->groundentity = -1; } else { pm->trace( &trace, pml.origin, pm->mins, pm->maxs, point ); pml.groundplane = trace.plane; pml.groundsurfFlags = trace.surfFlags; pml.groundcontents = trace.contents; if( (trace.fraction == 1) || (trace.plane.normal[2] < 0.7 && !trace.startsolid) ) { #ifdef JITSPOES_CLIPBUGFIX trace_t trace2; vec3_t mins, maxs; // try a slightly smaller bounding box -- this is to fix getting stuck up // on angled walls and not being able to move (like you're stuck in the air) mins[0] = pm->mins[0] ? pm->mins[0] + 1 : 0; mins[1] = pm->mins[1] ? pm->mins[1] + 1 : 0; mins[2] = pm->mins[2]; maxs[0] = pm->maxs[0] ? pm->maxs[0] - 1 : 0; maxs[1] = pm->maxs[1] ? pm->maxs[1] - 1 : 0; maxs[2] = pm->maxs[2]; pm->trace( &trace2, pml.origin, mins, maxs, point ); if( !(trace2.plane.normal[2] < 0.7f && !trace2.startsolid) ) { memcpy( &trace, &trace2, sizeof(trace) ); pml.groundplane = trace.plane; pml.groundsurfFlags = trace.surfFlags; pml.groundcontents = trace.contents; pm->groundentity = trace.ent; } #else //JITSPOES_CLIPBUGFIX pm->groundentity = -1; pm->s.pm_flags &= ~PMF_ON_GROUND; #endif //JITSPOES_CLIPBUGFIX } else { pm->groundentity = trace.ent; // hitting solid ground will end a waterjump if( pm->s.pm_flags & PMF_TIME_WATERJUMP ) { pm->s.pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_TELEPORT); pm->s.pm_time = 0; } if( !(pm->s.pm_flags & PMF_ON_GROUND) ) { // just hit the ground pm->s.pm_flags |= PMF_ON_GROUND; } } if( (pm->numtouch < MAXTOUCH) && (trace.fraction < 1.0) ) { pm->touchents[pm->numtouch] = trace.ent; pm->numtouch++; } } // // get waterlevel, accounting for ducking // pm->waterlevel = 0; pm->watertype = 0; sample2 = pm->viewheight - pm->mins[2]; sample1 = sample2 / 2; point[2] = pml.origin[2] + pm->mins[2] + 1; cont = pm->pointcontents( point ); if( cont & MASK_WATER ) { pm->watertype = cont; pm->waterlevel = 1; point[2] = pml.origin[2] + pm->mins[2] + sample1; cont = pm->pointcontents( point ); if( cont & MASK_WATER ) { pm->waterlevel = 2; point[2] = pml.origin[2] + pm->mins[2] + sample2; cont = pm->pointcontents( point ); if( cont & MASK_WATER ) pm->waterlevel = 3; } } } /* ================= PM_CheckWallJump -- By Kurim ================= */ static void PM_CheckWallJump( void ) { vec3_t normal; float length; vec3_t vWallJump; if( !(pm->cmd.buttons & BUTTON_SPECIAL) ) pm->s.pm_flags &= ~PMF_SPECIAL_HELD; if( pm->groundentity != -1 ) { pm->s.pm_flags &= ~PMF_WALLJUMPING; pm->s.stats[PM_STAT_WJCOUNT] = 0; } if( pm->s.pm_flags & PMF_WALLJUMPING && pml.velocity[2] < 0.0 ) pm->s.pm_flags &= ~PMF_WALLJUMPING; if( pm->s.pm_type >= PM_DEAD ) return; if( pm->s.stats[PM_STAT_STUN] > 0 ) return; if( pm->s.stats[PM_STAT_WJTIME] <= 0 ) // reset the wj count after wj delay pm->s.stats[PM_STAT_WJCOUNT] = 0; if( pm->groundentity == -1 && pm->cmd.buttons & BUTTON_SPECIAL ) { if( pm->s.stats[PM_STAT_WJCOUNT] >= pm->max_walljumps ) return; VectorClear( normal ); PlayerTouchWall( 12, 0.3f, &normal ); if( !VectorLength(normal) ) return; if( !(pm->s.pm_flags & PMF_SPECIAL_HELD) && !(pm->s.pm_flags & PMF_WALLJUMPING) ) { float oldupvelocity = pml.velocity[2]; pml.velocity[2] = 0.0; length = VectorNormalize2D( pml.velocity ); if( length < 320.0 ) length = 320.0; PM_ClipVelocity( pml.velocity, normal, pml.velocity, 1.01f ); VectorCopy( normal, vWallJump ); VectorMA( pml.velocity, 0.5, vWallJump, pml.velocity ); VectorNormalize( pml.velocity ); VectorScale( pml.velocity, length, pml.velocity ); pml.velocity[2] = (oldupvelocity > 370.0) ? oldupvelocity : 370.0; // jal: if we had a faster upwards speed, keep it pm->s.pm_flags |= PMF_WALLJUMPING; pm->s.pm_flags |= PMF_SPECIAL_HELD; pm->s.stats[PM_STAT_WJCOUNT]++; pm->s.stats[PM_STAT_WJTIME] = PM_WALLJUMP_TIMEDELAY; } } else pm->s.pm_flags &= ~PMF_WALLJUMPING; } /* ============= PM_CheckJump ============= */ static void PM_CheckJump( void ) { /* if (pm->s.pm_flags & PMF_TIME_LAND) { // hasn't been long enough since landing to jump again return; } */ if( pm->cmd.upmove < 10 ) { // not holding jump pm->s.pm_flags &= ~PMF_JUMP_HELD; return; } // must wait for jump to be released if( pm->s.pm_flags & PMF_JUMP_HELD ) return; if( pm->s.pm_type >= PM_DEAD ) return; if( pm->waterlevel >= 2 ) { // swimming, not jumping pm->groundentity = -1; return; } if( pm->groundentity == -1 ) return; // if ( (pm->s.pm_flags & PMF_DS_HELD) && pml.velocity[2] < 0.0 ) // Dash effect done, player is just falling // return; // if (pm->s.pm_flags & PMF_DASH_JUMP) // return; /* if ((pm->s.pm_flags & PMF_DS_HELD) && pm->groundentity == -1) // jump works differently if player is dashing... { pml.velocity[2] = pm_jump_velocity; // ...the jump effect is equivalent to a standard jump from the ground pm->s.pm_flags &= ~PMF_DASHING; // finish dash, now its just a jump pm->s.pm_flags |= PMF_JUMP_HELD; pm->s.pm_flags |= PMF_DASH_JUMP; pm->groundentity = -1; return; } */ pm->s.pm_flags |= PMF_JUMP_HELD; pm->groundentity = -1; if (pml.velocity[2] > 0) pml.velocity[2] += pm_jump_velocity; else pml.velocity[2] = pm_jump_velocity; // remove wj count (hopefully this fixes the wj-after-bunny bug) pm->s.pm_flags &= ~PMF_WALLJUMPING; pm->s.stats[PM_STAT_WJCOUNT] = 0; /* // check for double-jump if (pm->s.stats[PM_STAT_JUMPTIME] > 0) { pm->s.velocity[2] += cpm_pm_jump_z; pm->s.pm_flags |= PMF_DOUBLEJUMPED; } pm->s.stats[PM_STAT_JUMPTIME] = 400; // */ } /* ============= PM_CheckDash -- by Kurim ============= */ static void PM_CheckDash( void ) { float actual_velocity; float upspeed; vec3_t dashdir; if( !(pm->cmd.buttons & BUTTON_SPECIAL) ) pm->s.pm_flags &= ~PMF_SPECIAL_HELD; if( pm->s.pm_type >= PM_DEAD ) return; if( pm->s.stats[PM_STAT_STUN] > 0 ) return; if( pm->s.stats[PM_STAT_DASHTIME] > 0 ) return; if( pm->cmd.buttons & BUTTON_SPECIAL && pm->groundentity != -1) { if( pm->s.pm_flags & PMF_SPECIAL_HELD) return; pm->s.pm_flags |= PMF_DASHING; pm->s.pm_flags |= PMF_SPECIAL_HELD; pm->groundentity = -1; upspeed = dash_height + pml.velocity[2]; VectorSet( dashdir, 0.0, 0.0, 0.0 ); VectorMA( dashdir, (float)pm->cmd.forwardmove, pml.forward, dashdir ); VectorMA( dashdir, (float)pm->cmd.sidemove, pml.right, dashdir ); if( VectorLength( dashdir ) == 0.0 ) // if not moving, dash like a "forward dash" VectorCopy( pml.forward, dashdir ); dashdir[2] = 0.0; VectorNormalize( dashdir ); actual_velocity = VectorNormalize2D( pml.velocity ); if( actual_velocity <= dash_ups ) VectorScale( dashdir, dash_ups, dashdir ); else VectorScale( dashdir, actual_velocity, dashdir ); VectorCopy( dashdir, pml.velocity ); pml.velocity[2] = upspeed; pm->s.stats[PM_STAT_DASHTIME] = pm_dash_time; } else if( pm->groundentity == -1 ) pm->s.pm_flags &= ~PMF_DASHING; } /* ============= PM_CheckSpecialMovement ============= */ static void PM_CheckSpecialMovement( void ) { vec3_t spot; int cont; vec3_t flatforward; trace_t trace; if( pm->s.pm_time ) return; pml.ladder = qfalse; // check for ladder flatforward[0] = pml.forward[0]; flatforward[1] = pml.forward[1]; flatforward[2] = 0; VectorNormalize( flatforward ); VectorMA( pml.origin, 1, flatforward, spot ); pm->trace( &trace, pml.origin, pm->mins, pm->maxs, spot ); if( (trace.fraction < 1) && (trace.surfFlags & SURF_LADDER) ) pml.ladder = qtrue; // check for water jump if( pm->waterlevel != 2 ) return; VectorMA( pml.origin, 30, flatforward, spot ); spot[2] += 4; cont = pm->pointcontents( spot ); if( !(cont & CONTENTS_SOLID) ) return; spot[2] += 16; cont = pm->pointcontents( spot ); if( cont ) return; // jump out of water VectorScale( flatforward, 50, pml.velocity ); pml.velocity[2] = 350; pm->s.pm_flags |= PMF_TIME_WATERJUMP; pm->s.pm_time = 255; } /* =============== PM_FlyMove =============== */ static void PM_FlyMove( qboolean doclip ) { float speed, drop, friction, control, newspeed; float currentspeed, addspeed, accelspeed; int i; vec3_t wishvel; float fmove, smove; vec3_t wishdir; float wishspeed; vec3_t end; trace_t trace; pm->viewheight = playerbox_stand_viewheight; // friction speed = VectorLength( pml.velocity ); if( speed < 1 ) { VectorClear( pml.velocity ); } else { drop = 0; friction = pm_friction*1.5; // extra friction control = speed < pm_stopspeed ? pm_stopspeed : speed; drop += control*friction*pml.frametime; // scale the velocity newspeed = speed - drop; if( newspeed < 0 ) newspeed = 0; newspeed /= speed; VectorScale( pml.velocity, newspeed, pml.velocity ); } // accelerate fmove = pm->cmd.forwardmove; smove = pm->cmd.sidemove; VectorNormalize( pml.forward ); VectorNormalize( pml.right ); for( i = 0; i < 3; i++ ) wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; wishvel[2] += pm->cmd.upmove; VectorCopy( wishvel, wishdir ); wishspeed = VectorNormalize( wishdir ); // // clamp to server defined max speed // if( wishspeed > pm_maxspeed ) { wishspeed = pm_maxspeed/wishspeed; VectorScale( wishvel, wishspeed, wishvel ); wishspeed = pm_maxspeed; } currentspeed = DotProduct( pml.velocity, wishdir ); addspeed = wishspeed - currentspeed; if( addspeed <= 0 ) return; accelspeed = pm_accelerate*pml.frametime*wishspeed; if( accelspeed > addspeed ) accelspeed = addspeed; for( i=0 ; i<3 ; i++ ) pml.velocity[i] += accelspeed*wishdir[i]; if( doclip ) { for( i = 0; i < 3; i++ ) end[i] = pml.origin[i] + pml.frametime * pml.velocity[i]; pm->trace( &trace, pml.origin, pm->mins, pm->maxs, end ); VectorCopy( trace.endpos, pml.origin ); } else { // move VectorMA( pml.origin, pml.frametime, pml.velocity, pml.origin ); } } /* ============== PM_CheckDuck Sets mins, maxs, and pm->viewheight ============== */ static void PM_CheckDuck( void ) { trace_t trace; vec3_t end; //pm->mins[0] = playerbox_stand_mins[0]; //pm->mins[1] = playerbox_stand_mins[1]; //pm->maxs[0] = playerbox_stand_maxs[0]; //pm->maxs[1] = playerbox_stand_maxs[1]; VectorCopy( playerbox_stand_mins, pm->mins ); VectorCopy( playerbox_stand_maxs, pm->maxs ); if( pm->s.pm_type == PM_GIB ) { //VectorCopy( playerbox_gib_maxs, pm->maxs ); //VectorCopy( playerbox_gib_mins, pm->mins ); pm->mins[2] = playerbox_gib_mins[2]; pm->maxs[2] = playerbox_gib_maxs[2]; pm->viewheight = playerbox_gib_viewheight; return; } pm->mins[2] = playerbox_stand_mins[2]; if( pm->s.pm_type >= PM_DEAD ) { //VectorCopy( playerbox_stand_maxs, pm->maxs ); //VectorCopy( playerbox_stand_mins, pm->mins ); pm->maxs[2] = -8; pm->viewheight = 8; return; } // wsw: pb allow player to duck while in air if( pm->cmd.upmove < 0 )//&& (pm->s.pm_flags & PMF_ON_GROUND) ) { // duck pm->s.pm_flags |= PMF_DUCKED; } else { // stand up if possible if( pm->s.pm_flags & PMF_DUCKED ) { // try to stand up VectorCopy( pml.origin, end ); end[2] += playerbox_stand_maxs[2] - pm->maxs[2]; pm->trace( &trace, pml.origin, pm->mins, pm->maxs, end ); if( trace.fraction == 1 ) { pm->maxs[2] = playerbox_stand_maxs[2]; pm->s.pm_flags &= ~PMF_DUCKED; } } } if( pm->s.pm_flags & PMF_DUCKED ) { //VectorCopy( playerbox_crouch_mins, pm->mins ); //VectorCopy( playerbox_crouch_maxs, pm->maxs ); pm->maxs[2] = playerbox_crouch_maxs[2]; pm->viewheight = playerbox_crouch_viewheight; } else { //VectorCopy( playerbox_stand_mins, pm->mins ); //VectorCopy( playerbox_stand_maxs, pm->maxs ); pm->maxs[2] = playerbox_stand_maxs[2]; pm->viewheight = playerbox_stand_viewheight; } } /* ============== PM_DeadMove ============== */ static void PM_DeadMove( void ) { float forward; if( pm->groundentity == -1 ) return; // extra friction forward = VectorLength( pml.velocity ) - 20; if( forward <= 0 ) { VectorClear( pml.velocity ); } else { VectorNormalize( pml.velocity ); VectorScale( pml.velocity, forward, pml.velocity ); } } static qboolean PM_GoodPosition( void ) { trace_t trace; vec3_t origin, end; int i; if( pm->s.pm_type == PM_SPECTATOR ) return qtrue; for( i = 0; i < 3; i++ ) origin[i] = end[i] = pm->s.origin[i]*(1.0/16.0); pm->trace( &trace, origin, pm->mins, pm->maxs, end ); return !trace.allsolid; } /* ================ PM_SnapPosition On exit, the origin will have a value that is pre-quantized to the (1.0/16.0) precision of the network channel and in a valid position. ================ */ static void PM_SnapPosition( void ) { int sign[3]; int i, j, bits; int base[3]; // try all single bits first static const int jitterbits[8] = {0,4,1,2,3,5,6,7}; // snap velocity to eigths for( i = 0; i < 3; i++ ) pm->s.velocity[i] = (int)(pml.velocity[i]*16); for( i = 0; i < 3; i++ ) { if( pml.origin[i] >= 0 ) sign[i] = 1; else sign[i] = -1; pm->s.origin[i] = (int)(pml.origin[i]*16); if( pm->s.origin[i]*(1.0/16.0) == pml.origin[i] ) sign[i] = 0; } VectorCopy( pm->s.origin, base ); // try all combinations for( j = 0; j < 8; j++ ) { bits = jitterbits[j]; VectorCopy( base, pm->s.origin ); for( i = 0; i < 3; i++ ) if( bits & (1<s.origin[i] += sign[i]; if( PM_GoodPosition() ) return; } // go back to the last position VectorCopy( pml.previous_origin, pm->s.origin ); VectorClear( pm->s.velocity ); } /* ================ PM_InitialSnapPosition ================ */ static void PM_InitialSnapPosition( void ) { int x, y, z; int base[3]; static const int offset[3] = { 0, -1, 1 }; VectorCopy( pm->s.origin, base ); for( z = 0; z < 3; z++ ) { pm->s.origin[2] = base[2] + offset[ z ]; for( y = 0; y < 3; y++ ) { pm->s.origin[1] = base[1] + offset[ y ]; for( x = 0; x < 3; x++ ) { pm->s.origin[0] = base[0] + offset[ x ]; if( PM_GoodPosition() ) { pml.origin[0] = pm->s.origin[0]*(1.0/16.0); pml.origin[1] = pm->s.origin[1]*(1.0/16.0); pml.origin[2] = pm->s.origin[2]*(1.0/16.0); VectorCopy( pm->s.origin, pml.previous_origin ); return; } } } } } /* ================ PM_ClampAngles ================ */ static void PM_ClampAngles( void ) { int i; short temp; for( i = 0; i < 3; i++ ) { temp = pm->cmd.angles[i] + pm->s.delta_angles[i]; if ( i == PITCH ) { // don't let the player look up or down more than 90 degrees if ( temp > 16000 ) { pm->s.delta_angles[i] = 16000 - pm->cmd.angles[i]; temp = 16000; } else if ( temp < -16000 ) { pm->s.delta_angles[i] = -16000 - pm->cmd.angles[i]; temp = -16000; } } pm->viewangles[i] = SHORT2ANGLE( temp ); } AngleVectors( pm->viewangles, pml.forward, pml.right, pml.up ); } /* ================ Pmove Can be called by either the server or the client ================ */ void Pmove( pmove_t *pmove ) { pm = pmove; // clear results pm->numtouch = 0; VectorClear( pm->viewangles ); pm->viewheight = 0; pm->groundentity = -1; pm->watertype = 0; pm->waterlevel = 0; pm->step = qfalse; // clear all pmove local vars memset( &pml, 0, sizeof(pml) ); // convert origin and velocity to float values pml.origin[0] = pm->s.origin[0]*(1.0/16.0); pml.origin[1] = pm->s.origin[1]*(1.0/16.0); pml.origin[2] = pm->s.origin[2]*(1.0/16.0); pml.velocity[0] = pm->s.velocity[0]*(1.0/16.0); pml.velocity[1] = pm->s.velocity[1]*(1.0/16.0); pml.velocity[2] = pm->s.velocity[2]*(1.0/16.0); // save old org in case we get stuck VectorCopy( pm->s.origin, pml.previous_origin ); if( pm->s.stats[PM_STAT_SLOW] > 0 && pm->cmd.msec > 0 ) pm->cmd.msec /= 5; pml.frametime = pm->cmd.msec * 0.001; PM_ClampAngles(); if( pm->s.pm_type == PM_SPECTATOR ) { PM_FlyMove( qfalse ); PM_SnapPosition(); return; } if( pm->s.pm_type >= PM_DEAD ) { pm->cmd.forwardmove = 0; pm->cmd.sidemove = 0; pm->cmd.upmove = 0; } if( pm->s.pm_type >= PM_FREEZE ) // includes PM_CHASECAM return; // no movement at all // set mins, maxs, and viewheight PM_CheckDuck(); if( pm->snapinitial ) PM_InitialSnapPosition(); // set groundentity, watertype, and waterlevel PM_CategorizePosition(); if( pm->s.pm_type == PM_DEAD ) PM_DeadMove(); PM_CheckSpecialMovement(); // drop timing counter if( pm->s.pm_time ) { int msec; msec = pm->cmd.msec >> 3; if( !msec ) msec = 1; if( msec >= pm->s.pm_time ) { pm->s.pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_TELEPORT); pm->s.pm_time = 0; } else pm->s.pm_time -= msec; } /* // double-jump timer if (pm->s.stats[PM_STAT_JUMPTIME] > 0) pm->s.stats[PM_STAT_JUMPTIME] -= pm->cmd.msec; else if ( pm->s.pm_flags & PMF_DOUBLEJUMPED ) pm->s.pm_flags &= ~PMF_DOUBLEJUMPED; // */ if( pm->s.stats[PM_STAT_STUN] > 0 ) pm->s.stats[PM_STAT_STUN] -= pm->cmd.msec; if( pm->s.stats[PM_STAT_KNOCKBACK] > 0 ) pm->s.stats[PM_STAT_KNOCKBACK] -= pm->cmd.msec; if( pm->s.stats[PM_STAT_DASHTIME] > 0 ) pm->s.stats[PM_STAT_DASHTIME] -= pm->cmd.msec; if( pm->s.stats[PM_STAT_WJTIME] > 0 ) pm->s.stats[PM_STAT_WJTIME] -= pm->cmd.msec; if( pm->s.stats[PM_STAT_SLOW] > 0 ) pm->s.stats[PM_STAT_SLOW] -= pm->cmd.msec; if( pm->s.pm_flags & PMF_TIME_TELEPORT ) { // teleport pause stays exactly in place } else if( pm->s.pm_flags & PMF_TIME_WATERJUMP ) { // waterjump has no control, but falls pml.velocity[2] -= pm->s.gravity * pml.frametime; if( pml.velocity[2] < 0 ) { // cancel as soon as we are falling down again pm->s.pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_TELEPORT); pm->s.pm_time = 0; } PM_StepSlideMove(); } else { // Kurim // Keep this order ! PM_CheckJump(); PM_CheckDash(); PM_CheckWallJump(); PM_Friction(); if( pm->waterlevel >= 2 ) { PM_WaterMove(); } else { vec3_t angles; VectorCopy( pm->viewangles, angles ); if ( angles[PITCH] > 180 ) angles[PITCH] = angles[PITCH] - 360; angles[PITCH] /= 3; AngleVectors( angles, pml.forward, pml.right, pml.up ); PM_AirMove(); } } // set groundentity, watertype, and waterlevel for final spot PM_CategorizePosition(); PM_SnapPosition(); }