1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 //
23 // bg_slidemove.c -- part of bg_pmove functionality
24 
25 #include "../qcommon/q_shared.h"
26 #include "bg_public.h"
27 #include "bg_local.h"
28 
29 /*
30 
31 input: origin, velocity, bounds, groundPlane, trace function
32 
33 output: origin, velocity, impacts, stairup boolean
34 
35 */
36 
37 /*
38 ==================
39 PM_SlideMove
40 
41 Returns qtrue if the velocity was clipped in some way
42 ==================
43 */
44 #define	MAX_CLIP_PLANES	5
PM_SlideMove(qboolean gravity)45 qboolean	PM_SlideMove( qboolean gravity ) {
46 	int			bumpcount, numbumps;
47 	vec3_t		dir;
48 	float		d;
49 	int			numplanes;
50 	vec3_t		planes[MAX_CLIP_PLANES];
51 	vec3_t		primal_velocity;
52 	vec3_t		clipVelocity;
53 	int			i, j, k;
54 	trace_t	trace;
55 	vec3_t		end;
56 	float		time_left;
57 	float		into;
58 	vec3_t		endVelocity;
59 	vec3_t		endClipVelocity;
60 
61 	numbumps = 4;
62 
63 	VectorCopy (pm->ps->velocity, primal_velocity);
64 
65 	if ( gravity ) {
66 		VectorCopy( pm->ps->velocity, endVelocity );
67 		endVelocity[2] -= pm->ps->gravity * pml.frametime;
68 		pm->ps->velocity[2] = ( pm->ps->velocity[2] + endVelocity[2] ) * 0.5;
69 		primal_velocity[2] = endVelocity[2];
70 		if ( pml.groundPlane ) {
71 			// slide along the ground plane
72 			PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
73 				pm->ps->velocity, OVERCLIP );
74 		}
75 	}
76 
77 	time_left = pml.frametime;
78 
79 	// never turn against the ground plane
80 	if ( pml.groundPlane ) {
81 		numplanes = 1;
82 		VectorCopy( pml.groundTrace.plane.normal, planes[0] );
83 	} else {
84 		numplanes = 0;
85 	}
86 
87 	// never turn against original velocity
88 	VectorNormalize2( pm->ps->velocity, planes[numplanes] );
89 	numplanes++;
90 
91 	for ( bumpcount=0 ; bumpcount < numbumps ; bumpcount++ ) {
92 
93 		// calculate position we are trying to move to
94 		VectorMA( pm->ps->origin, time_left, pm->ps->velocity, end );
95 
96 		// see if we can make it there
97 		pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask);
98 
99 		if (trace.allsolid) {
100 			// entity is completely trapped in another solid
101 			pm->ps->velocity[2] = 0;	// don't build up falling damage, but allow sideways acceleration
102 			return qtrue;
103 		}
104 
105 		if (trace.fraction > 0) {
106 			// actually covered some distance
107 			VectorCopy (trace.endpos, pm->ps->origin);
108 		}
109 
110 		if (trace.fraction == 1) {
111 			 break;		// moved the entire distance
112 		}
113 
114 		// save entity for contact
115 		PM_AddTouchEnt( trace.entityNum );
116 
117 		time_left -= time_left * trace.fraction;
118 
119 		if (numplanes >= MAX_CLIP_PLANES) {
120 			// this shouldn't really happen
121 			VectorClear( pm->ps->velocity );
122 			return qtrue;
123 		}
124 
125 		//
126 		// if this is the same plane we hit before, nudge velocity
127 		// out along it, which fixes some epsilon issues with
128 		// non-axial planes
129 		//
130 		for ( i = 0 ; i < numplanes ; i++ ) {
131 			if ( DotProduct( trace.plane.normal, planes[i] ) > 0.99 ) {
132 				VectorAdd( trace.plane.normal, pm->ps->velocity, pm->ps->velocity );
133 				break;
134 			}
135 		}
136 		if ( i < numplanes ) {
137 			continue;
138 		}
139 		VectorCopy (trace.plane.normal, planes[numplanes]);
140 		numplanes++;
141 
142 		//
143 		// modify velocity so it parallels all of the clip planes
144 		//
145 
146 		// find a plane that it enters
147 		for ( i = 0 ; i < numplanes ; i++ ) {
148 			into = DotProduct( pm->ps->velocity, planes[i] );
149 			if ( into >= 0.1 ) {
150 				continue;		// move doesn't interact with the plane
151 			}
152 
153 			// see how hard we are hitting things
154 			if ( -into > pml.impactSpeed ) {
155 				pml.impactSpeed = -into;
156 			}
157 
158 			// slide along the plane
159 			PM_ClipVelocity (pm->ps->velocity, planes[i], clipVelocity, OVERCLIP );
160 
161 			// slide along the plane
162 			PM_ClipVelocity (endVelocity, planes[i], endClipVelocity, OVERCLIP );
163 
164 			// see if there is a second plane that the new move enters
165 			for ( j = 0 ; j < numplanes ; j++ ) {
166 				if ( j == i ) {
167 					continue;
168 				}
169 				if ( DotProduct( clipVelocity, planes[j] ) >= 0.1 ) {
170 					continue;		// move doesn't interact with the plane
171 				}
172 
173 				// try clipping the move to the plane
174 				PM_ClipVelocity( clipVelocity, planes[j], clipVelocity, OVERCLIP );
175 				PM_ClipVelocity( endClipVelocity, planes[j], endClipVelocity, OVERCLIP );
176 
177 				// see if it goes back into the first clip plane
178 				if ( DotProduct( clipVelocity, planes[i] ) >= 0 ) {
179 					continue;
180 				}
181 
182 				// slide the original velocity along the crease
183 				CrossProduct (planes[i], planes[j], dir);
184 				VectorNormalize( dir );
185 				d = DotProduct( dir, pm->ps->velocity );
186 				VectorScale( dir, d, clipVelocity );
187 
188 				CrossProduct (planes[i], planes[j], dir);
189 				VectorNormalize( dir );
190 				d = DotProduct( dir, endVelocity );
191 				VectorScale( dir, d, endClipVelocity );
192 
193 				// see if there is a third plane the the new move enters
194 				for ( k = 0 ; k < numplanes ; k++ ) {
195 					if ( k == i || k == j ) {
196 						continue;
197 					}
198 					if ( DotProduct( clipVelocity, planes[k] ) >= 0.1 ) {
199 						continue;		// move doesn't interact with the plane
200 					}
201 
202 					// stop dead at a tripple plane interaction
203 					VectorClear( pm->ps->velocity );
204 					return qtrue;
205 				}
206 			}
207 
208 			// if we have fixed all interactions, try another move
209 			VectorCopy( clipVelocity, pm->ps->velocity );
210 			VectorCopy( endClipVelocity, endVelocity );
211 			break;
212 		}
213 	}
214 
215 	if ( gravity ) {
216 		VectorCopy( endVelocity, pm->ps->velocity );
217 	}
218 
219 	// don't change velocity if in a timer (FIXME: is this correct?)
220 	if ( pm->ps->pm_time ) {
221 		VectorCopy( primal_velocity, pm->ps->velocity );
222 	}
223 
224 	return ( bumpcount != 0 );
225 }
226 
227 /*
228 ==================
229 PM_StepSlideMove
230 
231 ==================
232 */
PM_StepSlideMove(qboolean gravity)233 void PM_StepSlideMove( qboolean gravity ) {
234 	vec3_t		start_o, start_v;
235 	vec3_t		down_o, down_v;
236 	trace_t		trace;
237 //	float		down_dist, up_dist;
238 //	vec3_t		delta, delta2;
239 	vec3_t		up, down;
240 	float		stepSize;
241 
242 	VectorCopy (pm->ps->origin, start_o);
243 	VectorCopy (pm->ps->velocity, start_v);
244 
245 	if ( PM_SlideMove( gravity ) == 0 ) {
246 		return;		// we got exactly where we wanted to go first try
247 	}
248 
249 	VectorCopy(start_o, down);
250 	down[2] -= STEPSIZE;
251 	pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask);
252 	VectorSet(up, 0, 0, 1);
253 	// never step up when you still have up velocity
254 	if ( pm->ps->velocity[2] > 0 && (trace.fraction == 1.0 ||
255 										DotProduct(trace.plane.normal, up) < 0.7)) {
256 		return;
257 	}
258 
259 	VectorCopy (pm->ps->origin, down_o);
260 	VectorCopy (pm->ps->velocity, down_v);
261 
262 	VectorCopy (start_o, up);
263 	up[2] += STEPSIZE;
264 
265 	// test the player position if they were a stepheight higher
266 	pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask);
267 	if ( trace.allsolid ) {
268 		if ( pm->debugLevel ) {
269 			Com_Printf("%i:bend can't step\n", c_pmove);
270 		}
271 		return;		// can't step up
272 	}
273 
274 	stepSize = trace.endpos[2] - start_o[2];
275 	// try slidemove from this position
276 	VectorCopy (trace.endpos, pm->ps->origin);
277 	VectorCopy (start_v, pm->ps->velocity);
278 
279 	PM_SlideMove( gravity );
280 
281 	// push down the final amount
282 	VectorCopy (pm->ps->origin, down);
283 	down[2] -= stepSize;
284 	pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask);
285 	if ( !trace.allsolid ) {
286 		VectorCopy (trace.endpos, pm->ps->origin);
287 	}
288 	if ( trace.fraction < 1.0 ) {
289 		PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP );
290 	}
291 
292 #if 0
293 	// if the down trace can trace back to the original position directly, don't step
294 	pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, start_o, pm->ps->clientNum, pm->tracemask);
295 	if ( trace.fraction == 1.0 ) {
296 		// use the original move
297 		VectorCopy (down_o, pm->ps->origin);
298 		VectorCopy (down_v, pm->ps->velocity);
299 		if ( pm->debugLevel ) {
300 			Com_Printf("%i:bend\n", c_pmove);
301 		}
302 	} else
303 #endif
304 	{
305 		// use the step move
306 		float	delta;
307 
308 		delta = pm->ps->origin[2] - start_o[2];
309 		if ( delta > 2 ) {
310 			if ( delta < 7 ) {
311 				PM_AddEvent( EV_STEP_4 );
312 			} else if ( delta < 11 ) {
313 				PM_AddEvent( EV_STEP_8 );
314 			} else if ( delta < 15 ) {
315 				PM_AddEvent( EV_STEP_12 );
316 			} else {
317 				PM_AddEvent( EV_STEP_16 );
318 			}
319 		}
320 		if ( pm->debugLevel ) {
321 			Com_Printf("%i:stepped\n", c_pmove);
322 		}
323 	}
324 }
325 
326