1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 //
21 // pmove.c
22 //
23
24 #include "cg_local.h"
25
26 // all of the locals will be zeroed before each pmove, just to make damn sure
27 // we don't have any differences when running on client or server
28
29 typedef struct pMoveLocal_s {
30 vec3_t origin; // full float precision
31 vec3_t velocity; // full float precision
32
33 vec3_t forward, right, up;
34 float frameTime;
35
36 cBspSurface_t *groundSurface;
37 int groundContents;
38
39 svec3_t previousOrigin;
40 qBool ladder;
41 } pMoveLocal_t;
42
43 static pMoveNew_t *pm;
44 static pMoveLocal_t pml;
45
46 static float pmAirAcceleration = 0;
47
48 #define STEPSIZE 18
49 #define MIN_STEP_NORMAL 0.7f // can't step up onto very steep slopes
50 #define MAX_CLIP_PLANES 5
51
52 // movement parameters
53 #define PM_STOPSPEED 100.0f
54 #define PM_MAXSPEED 300.0f
55 #define PM_DUCKSPEED 100.0f
56 #define PM_ACCELERATE 10.0f
57 #define PM_WATERACCELERATE 10.0f
58 #define PM_FRICTION 6.0f
59 #define PM_WATERFRICTION 1.0f
60 #define PM_WATERSPEED 400.0f
61
62 /*
63 ==================
64 PM_ClipVelocity
65
66 Slide off of the impacting object
67 returns the blocked flags (1 = floor, 2 = step / wall)
68 ==================
69 */
PM_ClipVelocity(vec3_t in,vec3_t normal,vec3_t out,float overbounce)70 static void PM_ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
71 {
72 float backoff;
73 float change;
74 int i;
75
76 backoff = DotProduct (in, normal) * overbounce;
77
78 for (i=0 ; i<3 ; i++) {
79 change = normal[i]*backoff;
80 out[i] = in[i] - change;
81 if ((out[i] > -LARGE_EPSILON) && (out[i] < LARGE_EPSILON))
82 out[i] = 0;
83 }
84 }
85
86
87 /*
88 ==================
89 PM_StepSlideMove
90
91 Each intersection will try to step over the obstruction instead of
92 sliding along it.
93
94 Returns a new origin, velocity, and contact entity
95 Does not modify any world state?
96
97 Returns qTrue if going onto a step.
98 ==================
99 */
PM_StepSlideMove_(void)100 static qBool PM_StepSlideMove_ (void)
101 {
102 int bumpcount, numbumps;
103 vec3_t dir;
104 float d;
105 int numPlanes;
106 vec3_t planes[MAX_CLIP_PLANES];
107 vec3_t primal_velocity;
108 int i, j;
109 trace_t trace;
110 vec3_t end;
111 float time_left;
112 qBool step = qFalse;
113
114 numbumps = 4;
115
116 Vec3Copy (pml.velocity, primal_velocity);
117 numPlanes = 0;
118
119 time_left = pml.frameTime;
120
121 for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++) {
122 end[0] = pml.origin[0] + time_left * pml.velocity[0];
123 end[1] = pml.origin[1] + time_left * pml.velocity[1];
124 end[2] = pml.origin[2] + time_left * pml.velocity[2];
125
126 trace = pm->trace (pml.origin, pm->mins, pm->maxs, end);
127
128 if (trace.allSolid) {
129 // Entity is trapped in another solid
130 pml.velocity[2] = 0; // Don't build up falling damage
131 return 0;
132 }
133
134 if (trace.fraction > 0) {
135 // Actually covered some distance
136 Vec3Copy (trace.endPos, pml.origin);
137 numPlanes = 0;
138 }
139
140 if (trace.fraction == 1)
141 break; // Moved the entire distance
142
143 // Save entity for contact
144 if (pm->numTouch < MAXTOUCH && trace.ent) {
145 pm->touchEnts[pm->numTouch] = trace.ent;
146 pm->numTouch++;
147 }
148
149 if (trace.plane.normal[2] < MIN_STEP_NORMAL)
150 step = qTrue; // Step
151
152 time_left -= time_left * trace.fraction;
153
154 // Slide along this plane
155 if (numPlanes >= MAX_CLIP_PLANES) {
156 // This shouldn't really happen
157 Vec3Clear (pml.velocity);
158 break;
159 }
160
161 Vec3Copy (trace.plane.normal, planes[numPlanes]);
162 numPlanes++;
163
164 // modify original_velocity so it parallels all of the clip planes
165 for (i=0 ; i<numPlanes ; i++) {
166 PM_ClipVelocity (pml.velocity, planes[i], pml.velocity, 1.01f);
167 for (j=0 ; j<numPlanes ; j++) {
168 if (j != i) {
169 if (DotProduct (pml.velocity, planes[j]) < 0)
170 break; // not ok
171 }
172 }
173 if (j == numPlanes)
174 break;
175 }
176
177 if (i != numPlanes) {
178 // Go along this plane
179 }
180 else {
181 // Go along the crease
182 if (numPlanes != 2) {
183 Vec3Clear (pml.velocity);
184 break;
185 }
186 CrossProduct (planes[0], planes[1], dir);
187 d = DotProduct (dir, pml.velocity);
188 Vec3Scale (dir, d, pml.velocity);
189 }
190
191 /*
192 ** if velocity is against the original velocity, stop dead
193 ** to avoid tiny occilations in sloping corners
194 */
195 if (DotProduct (pml.velocity, primal_velocity) <= 0) {
196 Vec3Clear (pml.velocity);
197 break;
198 }
199 }
200
201 if (pm->state.pmTime)
202 Vec3Copy (primal_velocity, pml.velocity);
203
204 return step;
205 }
206
PM_StepSlideMove(void)207 static void PM_StepSlideMove (void)
208 {
209 vec3_t start_o, start_v;
210 vec3_t down_o, down_v;
211 trace_t trace;
212 float down_dist, up_dist;
213 vec3_t up, down;
214 qBool step;
215
216 Vec3Copy (pml.origin, start_o);
217 Vec3Copy (pml.velocity, start_v);
218
219 step = PM_StepSlideMove_ ();
220
221 Vec3Copy (pml.origin, down_o);
222 Vec3Copy (pml.velocity, down_v);
223
224 Vec3Copy (start_o, up);
225 up[2] += STEPSIZE;
226
227 trace = pm->trace (up, pm->mins, pm->maxs, up);
228 if (trace.allSolid)
229 return; // Can't step up
230
231 // Try sliding above
232 Vec3Copy (up, pml.origin);
233 Vec3Copy (start_v, pml.velocity);
234
235 PM_StepSlideMove_ ();
236
237 // Push down the final amount
238 Vec3Copy (pml.origin, down);
239 down[2] -= STEPSIZE;
240 trace = pm->trace (pml.origin, pm->mins, pm->maxs, down);
241 if (!trace.allSolid)
242 Vec3Copy (trace.endPos, pml.origin);
243
244 Vec3Copy (pml.origin, up);
245
246 // Decide which one went farther
247 down_dist = (down_o[0] - start_o[0])*(down_o[0] - start_o[0])
248 + (down_o[1] - start_o[1])*(down_o[1] - start_o[1]);
249 up_dist = (up[0] - start_o[0])*(up[0] - start_o[0])
250 + (up[1] - start_o[1])*(up[1] - start_o[1]);
251
252 if (down_dist > up_dist || trace.plane.normal[2] < MIN_STEP_NORMAL) {
253 Vec3Copy (down_o, pml.origin);
254 Vec3Copy (down_v, pml.velocity);
255 return;
256 }
257
258 if (step || trace.plane.normal[2] == 1)
259 pm->step = qTrue;
260
261 // !! Special case !!
262 // if we were walking along a plane, then we need to copy the Z over
263 pml.velocity[2] = down_v[2];
264 }
265
266
267 /*
268 ==================
269 PM_Friction
270
271 Handles both ground friction and water friction
272 ==================
273 */
PM_Friction(void)274 static void PM_Friction (void)
275 {
276 float *vel;
277 float speed, newspeed, control;
278 float friction;
279 float drop;
280
281 vel = pml.velocity;
282
283 speed = Vec3Length (vel);
284 if (speed < 1) {
285 vel[0] = 0;
286 vel[1] = 0;
287 return;
288 }
289
290 drop = 0;
291
292 // Apply ground friction
293 if ((pm->groundEntity && pml.groundSurface && !(pml.groundSurface->flags & SURF_TEXINFO_SLICK)) || pml.ladder) {
294 friction = PM_FRICTION;
295 control = (speed < PM_STOPSPEED) ? PM_STOPSPEED : speed;
296 drop += control*friction*pml.frameTime;
297 }
298
299 // Apply water friction
300 if (pm->waterLevel && !pml.ladder)
301 drop += speed*PM_WATERFRICTION*pm->waterLevel*pml.frameTime;
302
303 // Scale the velocity
304 newspeed = speed - drop;
305 if (newspeed < 0)
306 newspeed = 0;
307
308 newspeed /= speed;
309
310 vel[0] = vel[0] * newspeed;
311 vel[1] = vel[1] * newspeed;
312 vel[2] = vel[2] * newspeed;
313 }
314
315
316 /*
317 ==============
318 PM_Accelerate
319
320 Handles user intended acceleration
321 ==============
322 */
PM_Accelerate(vec3_t wishdir,float wishspeed,float accel)323 static void PM_Accelerate (vec3_t wishdir, float wishspeed, float accel)
324 {
325 float addspeed, accelspeed, currentspeed;
326
327 currentspeed = DotProduct (pml.velocity, wishdir);
328 addspeed = wishspeed - currentspeed;
329 if (addspeed <= 0)
330 return;
331
332 accelspeed = accel*pml.frameTime*wishspeed;
333 if (accelspeed > addspeed)
334 accelspeed = addspeed;
335
336 pml.velocity[0] += accelspeed*wishdir[0];
337 pml.velocity[1] += accelspeed*wishdir[1];
338 pml.velocity[2] += accelspeed*wishdir[2];
339 }
340
341
342 /*
343 ==============
344 PM_AirAccelerate
345
346 Handles user intended air acceleration
347 ==============
348 */
PM_AirAccelerate(vec3_t wishdir,float wishspeed,float accel)349 static void PM_AirAccelerate (vec3_t wishdir, float wishspeed, float accel)
350 {
351 float addspeed, accelspeed;
352 float currentspeed, wishspd = wishspeed;
353
354 if (wishspd > 30)
355 wishspd = 30;
356 currentspeed = DotProduct (pml.velocity, wishdir);
357 addspeed = wishspd - currentspeed;
358 if (addspeed <= 0)
359 return;
360
361 accelspeed = accel * wishspeed * pml.frameTime;
362 if (accelspeed > addspeed)
363 accelspeed = addspeed;
364
365 pml.velocity[0] += accelspeed*wishdir[0];
366 pml.velocity[1] += accelspeed*wishdir[1];
367 pml.velocity[2] += accelspeed*wishdir[2];
368 }
369
370
371 /*
372 =============
373 PM_AddCurrents
374 =============
375 */
PM_AddCurrents(vec3_t wishvel)376 static void PM_AddCurrents (vec3_t wishvel)
377 {
378 vec3_t v;
379 float s;
380
381 // Account for ladders
382 if (pml.ladder && fabs (pml.velocity[2]) <= 200) {
383 if ((pm->viewAngles[PITCH] <= -15) && (pm->cmd.forwardMove > 0))
384 wishvel[2] = 200;
385 else if ((pm->viewAngles[PITCH] >= 15) && (pm->cmd.forwardMove > 0))
386 wishvel[2] = -200;
387 else if (pm->cmd.upMove > 0)
388 wishvel[2] = 200;
389 else if (pm->cmd.upMove < 0)
390 wishvel[2] = -200;
391 else
392 wishvel[2] = 0;
393
394 // Limit horizontal speed when on a ladder
395 if (wishvel[0] < -25)
396 wishvel[0] = -25;
397 else if (wishvel[0] > 25)
398 wishvel[0] = 25;
399
400 if (wishvel[1] < -25)
401 wishvel[1] = -25;
402 else if (wishvel[1] > 25)
403 wishvel[1] = 25;
404 }
405
406 // Add water currents
407 if (pm->waterType & MASK_CURRENT) {
408 Vec3Clear (v);
409
410 if (pm->waterType & CONTENTS_CURRENT_0) v[0] += 1;
411 if (pm->waterType & CONTENTS_CURRENT_90) v[1] += 1;
412 if (pm->waterType & CONTENTS_CURRENT_180) v[0] -= 1;
413 if (pm->waterType & CONTENTS_CURRENT_270) v[1] -= 1;
414 if (pm->waterType & CONTENTS_CURRENT_UP) v[2] += 1;
415 if (pm->waterType & CONTENTS_CURRENT_DOWN) v[2] -= 1;
416
417 s = PM_WATERSPEED;
418 if ((pm->waterLevel == 1) && (pm->groundEntity))
419 s /= 2;
420
421 Vec3MA (wishvel, s, v, wishvel);
422 }
423
424 // Add conveyor belt velocities
425 if (pm->groundEntity) {
426 Vec3Clear (v);
427
428 if (pml.groundContents & CONTENTS_CURRENT_0) v[0] += 1;
429 if (pml.groundContents & CONTENTS_CURRENT_90) v[1] += 1;
430 if (pml.groundContents & CONTENTS_CURRENT_180) v[0] -= 1;
431 if (pml.groundContents & CONTENTS_CURRENT_270) v[1] -= 1;
432 if (pml.groundContents & CONTENTS_CURRENT_UP) v[2] += 1;
433 if (pml.groundContents & CONTENTS_CURRENT_DOWN) v[2] -= 1;
434
435 Vec3MA (wishvel, 100 /* pm->groundEntity->speed */, v, wishvel);
436 }
437 }
438
439
440 /*
441 ===================
442 PM_WaterMove
443 ===================
444 */
PM_WaterMove(void)445 static void PM_WaterMove (void)
446 {
447 vec3_t wishvel, wishdir;
448 float wishspeed;
449
450 // User intentions
451 wishvel[0] = pml.forward[0]*pm->cmd.forwardMove + pml.right[0]*pm->cmd.sideMove;
452 wishvel[1] = pml.forward[1]*pm->cmd.forwardMove + pml.right[1]*pm->cmd.sideMove;
453 wishvel[2] = pml.forward[2]*pm->cmd.forwardMove + pml.right[2]*pm->cmd.sideMove;
454
455 if (!pm->cmd.forwardMove && !pm->cmd.sideMove && !pm->cmd.upMove)
456 wishvel[2] -= 60; // Drift towards bottom
457 else
458 wishvel[2] += pm->cmd.upMove;
459
460 PM_AddCurrents (wishvel);
461
462 Vec3Copy (wishvel, wishdir);
463 wishspeed = VectorNormalizef (wishdir, wishdir);
464
465 if (wishspeed > PM_MAXSPEED) {
466 Vec3Scale (wishvel, PM_MAXSPEED/wishspeed, wishvel);
467 wishspeed = PM_MAXSPEED;
468 }
469 wishspeed *= 0.5f;
470
471 PM_Accelerate (wishdir, wishspeed, PM_WATERACCELERATE);
472
473 PM_StepSlideMove ();
474 }
475
476
477 /*
478 ===================
479 PM_AirMove
480 ===================
481 */
PM_AirMove(void)482 static void PM_AirMove (void)
483 {
484 vec3_t wishvel, wishdir;
485 float fmove, smove;
486 float wishspeed;
487 float maxspeed;
488
489 fmove = pm->cmd.forwardMove;
490 smove = pm->cmd.sideMove;
491
492 wishvel[0] = pml.forward[0]*fmove + pml.right[0]*smove;
493 wishvel[1] = pml.forward[1]*fmove + pml.right[1]*smove;
494 wishvel[2] = 0;
495
496 PM_AddCurrents (wishvel);
497
498 Vec3Copy (wishvel, wishdir);
499 wishspeed = VectorNormalizef (wishdir, wishdir);
500
501 // Clamp to server defined max speed
502 maxspeed = (pm->state.pmFlags & PMF_DUCKED) ? PM_DUCKSPEED : PM_MAXSPEED;
503
504 if (wishspeed > maxspeed) {
505 Vec3Scale (wishvel, maxspeed/wishspeed, wishvel);
506 wishspeed = maxspeed;
507 }
508
509 if (pml.ladder) {
510 PM_Accelerate (wishdir, wishspeed, PM_ACCELERATE);
511 if (!wishvel[2]) {
512 if (pml.velocity[2] > 0) {
513 pml.velocity[2] -= pm->state.gravity * pml.frameTime;
514 if (pml.velocity[2] < 0)
515 pml.velocity[2] = 0;
516 }
517 else {
518 pml.velocity[2] += pm->state.gravity * pml.frameTime;
519 if (pml.velocity[2] > 0)
520 pml.velocity[2] = 0;
521 }
522 }
523
524 PM_StepSlideMove ();
525 }
526 else if (pm->groundEntity) {
527 // Walking on ground
528 pml.velocity[2] = 0; //!!! this is before the accel
529 PM_Accelerate (wishdir, wishspeed, PM_ACCELERATE);
530
531 if (pm->state.gravity > 0)
532 pml.velocity[2] = 0;
533 else
534 pml.velocity[2] -= pm->state.gravity * pml.frameTime;
535
536 if (!pml.velocity[0] && !pml.velocity[1])
537 return;
538 PM_StepSlideMove ();
539 }
540 else {
541 // Not on ground, so little effect on velocity
542 if (pmAirAcceleration)
543 PM_AirAccelerate (wishdir, wishspeed, PM_ACCELERATE);
544 else
545 PM_Accelerate (wishdir, wishspeed, 1);
546
547 // Add gravity
548 pml.velocity[2] -= pm->state.gravity * pml.frameTime;
549 PM_StepSlideMove ();
550 }
551 }
552
553
554 /*
555 =============
556 PM_CatagorizePosition
557 =============
558 */
PM_CatagorizePosition(void)559 static void PM_CatagorizePosition (void)
560 {
561 vec3_t point;
562 int cont;
563 trace_t trace;
564 float sample1;
565 float sample2;
566
567 // If the player hull point one unit down is solid, the player is on ground
568 // see if standing on something solid
569 point[0] = pml.origin[0];
570 point[1] = pml.origin[1];
571 point[2] = pml.origin[2] - 0.25;
572
573 if (pml.velocity[2] > 180) {
574 pm->state.pmFlags &= ~PMF_ON_GROUND;
575 pm->groundEntity = NULL;
576 }
577 else {
578 trace = pm->trace (pml.origin, pm->mins, pm->maxs, point);
579 pml.groundSurface = trace.surface;
580 pml.groundContents = trace.contents;
581
582 if (!trace.ent || (trace.plane.normal[2] < MIN_STEP_NORMAL && !trace.startSolid)) {
583 pm->groundEntity = NULL;
584 pm->state.pmFlags &= ~PMF_ON_GROUND;
585 }
586 else {
587 pm->groundEntity = trace.ent;
588
589 // Hitting solid ground will end a waterjump
590 if (pm->state.pmFlags & PMF_TIME_WATERJUMP) {
591 pm->state.pmFlags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_TELEPORT);
592 pm->state.pmTime = 0;
593 }
594
595 if (!(pm->state.pmFlags & PMF_ON_GROUND)) {
596 // Just hit the ground
597 pm->state.pmFlags |= PMF_ON_GROUND;
598 // Don't do landing time if we were just going down a slope
599 if (pml.velocity[2] < -200 && !pm->strafeHack) {
600 pm->state.pmFlags |= PMF_TIME_LAND;
601 // Don't allow another jump for a little while
602 if (pml.velocity[2] < -400)
603 pm->state.pmTime = 25;
604 else
605 pm->state.pmTime = 18;
606 }
607 }
608 }
609
610 if (pm->numTouch < MAXTOUCH && trace.ent) {
611 pm->touchEnts[pm->numTouch] = trace.ent;
612 pm->numTouch++;
613 }
614 }
615
616 // Get waterLevel, accounting for ducking
617 pm->waterLevel = 0;
618 pm->waterType = 0;
619
620 sample2 = pm->viewHeight - pm->mins[2];
621 sample1 = sample2 / 2;
622
623 point[2] = pml.origin[2] + pm->mins[2] + 1;
624 cont = pm->pointContents (point);
625
626 if (cont & MASK_WATER) {
627 pm->waterType = cont;
628 pm->waterLevel = 1;
629 point[2] = pml.origin[2] + pm->mins[2] + sample1;
630 cont = pm->pointContents (point);
631 if (cont & MASK_WATER) {
632 pm->waterLevel = 2;
633 point[2] = pml.origin[2] + pm->mins[2] + sample2;
634 cont = pm->pointContents (point);
635 if (cont & MASK_WATER)
636 pm->waterLevel = 3;
637 }
638 }
639 }
640
641
642 /*
643 =============
644 PM_CheckJump
645 =============
646 */
PM_CheckJump(void)647 static void PM_CheckJump (void)
648 {
649 if (pm->state.pmFlags & PMF_TIME_LAND) {
650 // Hasn't been long enough since landing to jump again
651 return;
652 }
653
654 if (pm->cmd.upMove < 10) {
655 // Not holding jump
656 pm->state.pmFlags &= ~PMF_JUMP_HELD;
657 return;
658 }
659
660 // Must wait for jump to be released
661 if (pm->state.pmFlags & PMF_JUMP_HELD)
662 return;
663
664 if (pm->state.pmType == PMT_DEAD)
665 return;
666
667 if (pm->waterLevel >= 2) {
668 // Swimming, not jumping
669 pm->groundEntity = NULL;
670
671 if (pml.velocity[2] <= -300)
672 return;
673
674 if (pm->waterType == CONTENTS_WATER)
675 pml.velocity[2] = 100;
676 else if (pm->waterType == CONTENTS_SLIME)
677 pml.velocity[2] = 80;
678 else
679 pml.velocity[2] = 50;
680 return;
681 }
682
683 if (pm->groundEntity == NULL)
684 return; // In air, so no effect
685
686 pm->state.pmFlags |= PMF_JUMP_HELD;
687
688 pm->groundEntity = NULL;
689 pml.velocity[2] += 270;
690 if (pml.velocity[2] < 270)
691 pml.velocity[2] = 270;
692
693 // This is so hacky my eyes hurt just looking at it
694 if (!cgi.Cvar_GetIntegerValue ("dedicated") && glm_jumppred->floatVal && !cgi.Com_ServerState () && cg.currGameMod == GAME_MOD_GLOOM) {
695 switch (cg.gloomClassType) {
696 case GLM_HATCHLING: pml.velocity[2] *= 1.5f; break;
697 case GLM_DRONE: pml.velocity[2] *= 1.4f; break;
698 case GLM_KAMIKAZE: pml.velocity[2] *= 1.4f; break;
699 case GLM_STINGER: pml.velocity[2] *= 1.35f; break;
700 case GLM_GUARDIAN: pml.velocity[2] *= 1.2f; break;
701 case GLM_STALKER: pml.velocity[2] *= 0.5f; break;
702 case GLM_BREEDER: pml.velocity[2] *= 0.7f; break;
703 case GLM_HT: pml.velocity[2] *= 0.8f; break;
704 case GLM_COMMANDO: pml.velocity[2] *= 1.2f; break;
705 case GLM_EXTERM: pml.velocity[2] *= 0.9f; break;
706 case GLM_MECH: pml.velocity[2] *= 0.4f; break;
707 case GLM_WRAITH: pml.velocity[2] *= 1.4f; break;
708
709 case GLM_GRUNT:
710 case GLM_ST:
711 case GLM_ENGINEER:
712 case GLM_BIOTECH:
713 case GLM_DEFAULT:
714 case GLM_OBSERVER:
715 default:
716 break;
717 }
718 }
719 }
720
721
722 /*
723 =============
724 PM_CheckSpecialMovement
725 =============
726 */
PM_CheckSpecialMovement(void)727 static void PM_CheckSpecialMovement (void)
728 {
729 vec3_t spot;
730 int cont;
731 vec3_t flatforward;
732 trace_t trace;
733
734 if (pm->state.pmTime)
735 return;
736
737 pml.ladder = qFalse;
738
739 // Check for ladder
740 flatforward[0] = pml.forward[0];
741 flatforward[1] = pml.forward[1];
742 flatforward[2] = 0;
743 VectorNormalizef (flatforward, flatforward);
744
745 Vec3MA (pml.origin, 1, flatforward, spot);
746 trace = pm->trace (pml.origin, pm->mins, pm->maxs, spot);
747 if ((trace.fraction < 1) && (trace.contents & CONTENTS_LADDER))
748 pml.ladder = qTrue;
749
750 // Check for water jump
751 if (pm->waterLevel != 2)
752 return;
753
754 Vec3MA (pml.origin, 30, flatforward, spot);
755 spot[2] += 4;
756 cont = pm->pointContents (spot);
757 if (!(cont & CONTENTS_SOLID))
758 return;
759
760 spot[2] += 16;
761 cont = pm->pointContents (spot);
762 if (cont)
763 return;
764
765 // Jump out of water
766 Vec3Scale (flatforward, 50, pml.velocity);
767 pml.velocity[2] = 350;
768
769 pm->state.pmFlags |= PMF_TIME_WATERJUMP;
770 pm->state.pmTime = 255;
771 }
772
773
774 /*
775 ===============
776 PM_FlyMove
777 ===============
778 */
PM_FlyMove(qBool doClip)779 static void PM_FlyMove (qBool doClip)
780 {
781 float speed, drop, friction, control, newspeed;
782 float currentspeed, addspeed, accelspeed;
783 vec3_t wishvel, wishdir, end;
784 float fmove, smove, wishspeed;
785 trace_t trace;
786
787 pm->viewHeight = 22;
788
789 // friction
790 speed = Vec3Length (pml.velocity);
791 if (speed < 1)
792 Vec3Clear (pml.velocity);
793 else {
794 drop = 0;
795
796 friction = PM_FRICTION*1.5; // extra friction
797 control = (speed < PM_STOPSPEED) ? PM_STOPSPEED : speed;
798 drop += control*friction*pml.frameTime;
799
800 // scale the velocity
801 newspeed = speed - drop;
802 if (newspeed < 0)
803 newspeed = 0;
804 newspeed /= speed;
805
806 Vec3Scale (pml.velocity, newspeed, pml.velocity);
807 }
808
809 // accelerate
810 fmove = pm->cmd.forwardMove;
811 smove = pm->cmd.sideMove;
812
813 VectorNormalizef (pml.forward, pml.forward);
814 VectorNormalizef (pml.right, pml.right);
815
816 wishvel[0] = pml.forward[0]*fmove + pml.right[0]*smove;
817 wishvel[1] = pml.forward[1]*fmove + pml.right[1]*smove;
818 wishvel[2] = pml.forward[2]*fmove + pml.right[2]*smove + pm->cmd.upMove;
819
820 Vec3Copy (wishvel, wishdir);
821 wishspeed = VectorNormalizef (wishdir, wishdir);
822
823 // clamp to server defined max speed
824 if (wishspeed > PM_MAXSPEED) {
825 Vec3Scale (wishvel, PM_MAXSPEED/wishspeed, wishvel);
826 wishspeed = PM_MAXSPEED;
827 }
828
829 currentspeed = DotProduct (pml.velocity, wishdir);
830 addspeed = wishspeed - currentspeed;
831 if (addspeed <= 0)
832 return;
833
834 accelspeed = PM_ACCELERATE*pml.frameTime*wishspeed;
835 if (accelspeed > addspeed)
836 accelspeed = addspeed;
837
838 pml.velocity[0] += accelspeed*wishdir[0];
839 pml.velocity[1] += accelspeed*wishdir[1];
840 pml.velocity[2] += accelspeed*wishdir[2];
841
842 if (doClip) {
843 end[0] = pml.origin[0] + pml.frameTime * pml.velocity[0];
844 end[1] = pml.origin[1] + pml.frameTime * pml.velocity[1];
845 end[2] = pml.origin[2] + pml.frameTime * pml.velocity[2];
846
847 trace = pm->trace (pml.origin, pm->mins, pm->maxs, end);
848
849 Vec3Copy (trace.endPos, pml.origin);
850 }
851 else {
852 // move
853 Vec3MA (pml.origin, pml.frameTime, pml.velocity, pml.origin);
854 }
855 }
856
857
858 /*
859 ==============
860 PM_CheckDuck
861
862 Sets mins, maxs, and pm->viewHeight
863 ==============
864 */
PM_CheckDuck(void)865 static void PM_CheckDuck (void)
866 {
867 trace_t trace;
868
869 pm->mins[0] = -16;
870 pm->mins[1] = -16;
871
872 pm->maxs[0] = 16;
873 pm->maxs[1] = 16;
874
875 if (pm->state.pmType == PMT_GIB) {
876 pm->mins[2] = 0;
877 pm->maxs[2] = 16;
878 pm->viewHeight = 8;
879 return;
880 }
881
882 pm->mins[2] = -24;
883
884 if (pm->state.pmType == PMT_DEAD) {
885 pm->state.pmFlags |= PMF_DUCKED;
886 }
887 else if (pm->cmd.upMove < 0 && (pm->state.pmFlags & PMF_ON_GROUND)) {
888 // duck
889 pm->state.pmFlags |= PMF_DUCKED;
890 }
891 else {
892 // stand up if possible
893 if (pm->state.pmFlags & PMF_DUCKED) {
894 // try to stand up
895 pm->maxs[2] = 32;
896 trace = pm->trace (pml.origin, pm->mins, pm->maxs, pml.origin);
897 if (!trace.allSolid)
898 pm->state.pmFlags &= ~PMF_DUCKED;
899 }
900 }
901
902 if (pm->state.pmFlags & PMF_DUCKED) {
903 pm->maxs[2] = 4;
904 pm->viewHeight = -2;
905 }
906 else {
907 pm->maxs[2] = 32;
908 pm->viewHeight = 22;
909 }
910 }
911
912
913 /*
914 ==============
915 PM_DeadMove
916 ==============
917 */
PM_DeadMove(void)918 static void PM_DeadMove (void)
919 {
920 float forward;
921
922 if (!pm->groundEntity)
923 return;
924
925 // extra friction
926 forward = Vec3Length (pml.velocity);
927 forward -= 20;
928 if (forward <= 0)
929 Vec3Clear (pml.velocity);
930 else {
931 VectorNormalizef (pml.velocity, pml.velocity);
932 Vec3Scale (pml.velocity, forward, pml.velocity);
933 }
934 }
935
936
937 /*
938 ================
939 PM_GoodPosition
940 ================
941 */
PM_GoodPosition(void)942 static qBool PM_GoodPosition (void)
943 {
944 trace_t trace;
945 vec3_t origin, end;
946
947 if (pm->state.pmType == PMT_SPECTATOR)
948 return qTrue;
949
950 origin[0] = end[0] = pm->state.origin[0]*(1.0f/8.0f);
951 origin[1] = end[1] = pm->state.origin[1]*(1.0f/8.0f);
952 origin[2] = end[2] = pm->state.origin[2]*(1.0f/8.0f);
953 trace = pm->trace (origin, pm->mins, pm->maxs, end);
954
955 return !trace.allSolid;
956 }
957
958
959 /*
960 ================
961 PM_SnapPosition
962
963 On exit, the origin will have a value that is pre-quantized to the (1.0f/8.0f)
964 precision of the network channel and in a valid position.
965 ================
966 */
PM_SnapPosition(void)967 static void PM_SnapPosition (void)
968 {
969 int sign[3];
970 int i, j, bits;
971 int16 base[3];
972 // try all single bits first
973 static int jitterbits[8] = {0,4,1,2,3,5,6,7};
974
975 // snap velocity to eigths
976 pm->state.velocity[0] = (int)(pml.velocity[0]*8);
977 pm->state.velocity[1] = (int)(pml.velocity[1]*8);
978 pm->state.velocity[2] = (int)(pml.velocity[2]*8);
979
980 for (i=0 ; i<3 ; i++) {
981 if (pml.origin[i] >= 0)
982 sign[i] = 1;
983 else
984 sign[i] = -1;
985 pm->state.origin[i] = (int)(pml.origin[i]*8);
986 if (pm->state.origin[i]*(1.0f/8.0f) == pml.origin[i])
987 sign[i] = 0;
988 }
989 Vec3Copy (pm->state.origin, base);
990
991 // try all combinations
992 for (j=0 ; j<8 ; j++) {
993 bits = jitterbits[j];
994 Vec3Copy (base, pm->state.origin);
995 for (i=0 ; i<3 ; i++)
996 if (bits & (1<<i))
997 pm->state.origin[i] += sign[i];
998
999 if (PM_GoodPosition ())
1000 return;
1001 }
1002
1003 // go back to the last position
1004 Vec3Copy (pml.previousOrigin, pm->state.origin);
1005 }
1006
1007
1008 /*
1009 ================
1010 PM_InitialSnapPosition
1011 ================
1012 */
PM_InitialSnapPosition(void)1013 static void PM_InitialSnapPosition (void)
1014 {
1015 int x, y, z;
1016 int16 base[3];
1017 static int offset[3] = { 0, -1, 1 };
1018
1019 Vec3Copy (pm->state.origin, base);
1020
1021 for (z=0 ; z<3 ; z++) {
1022 pm->state.origin[2] = base[2] + offset[z];
1023 for (y=0 ; y<3 ; y++) {
1024 pm->state.origin[1] = base[1] + offset[y];
1025 for (x=0 ; x<3 ; x++) {
1026 pm->state.origin[0] = base[0] + offset[x];
1027 if (PM_GoodPosition ()) {
1028 pml.origin[0] = pm->state.origin[0]*(1.0f/8.0f);
1029 pml.origin[1] = pm->state.origin[1]*(1.0f/8.0f);
1030 pml.origin[2] = pm->state.origin[2]*(1.0f/8.0f);
1031 Vec3Copy (pm->state.origin, pml.previousOrigin);
1032 return;
1033 }
1034 }
1035 }
1036 }
1037
1038 Com_DevPrintf (PRNT_WARNING, "Bad InitialSnapPosition\n");
1039 }
1040
1041
1042 /*
1043 ================
1044 PM_ClampAngles
1045 ================
1046 */
PM_ClampAngles(void)1047 static void PM_ClampAngles (void)
1048 {
1049 int16 temp;
1050 int i;
1051
1052 if (pm->state.pmFlags & PMF_TIME_TELEPORT) {
1053 pm->viewAngles[YAW] = SHORT2ANGLE (pm->cmd.angles[YAW] + pm->state.deltaAngles[YAW]);
1054 pm->viewAngles[PITCH] = 0;
1055 pm->viewAngles[ROLL] = 0;
1056 }
1057 else {
1058 // circularly clamp the angles with deltas
1059 for (i=0 ; i<3 ; i++) {
1060 temp = pm->cmd.angles[i] + pm->state.deltaAngles[i];
1061 pm->viewAngles[i] = SHORT2ANGLE(temp);
1062 }
1063
1064 // don't let the player look up or down more than 90 degrees
1065 if ((pm->viewAngles[PITCH] > 89) && (pm->viewAngles[PITCH] < 180))
1066 pm->viewAngles[PITCH] = 89;
1067 else if ((pm->viewAngles[PITCH] < 271) && (pm->viewAngles[PITCH] >= 180))
1068 pm->viewAngles[PITCH] = 271;
1069 }
1070
1071 Angles_Vectors (pm->viewAngles, pml.forward, pml.right, pml.up);
1072 }
1073
1074
1075 /*
1076 ================
1077 Pmove
1078
1079 Can be called by either the server or the client
1080 ================
1081 */
Pmove(pMoveNew_t * pMove,float airAcceleration)1082 void Pmove (pMoveNew_t *pMove, float airAcceleration)
1083 {
1084 pm = pMove;
1085 pmAirAcceleration = airAcceleration;
1086
1087 // clear results
1088 pm->numTouch = 0;
1089 Vec3Clear (pm->viewAngles);
1090 pm->viewHeight = 0;
1091 pm->groundEntity = 0;
1092 pm->waterType = 0;
1093 pm->waterLevel = 0;
1094 pm->step = qFalse;
1095
1096 // clear all pmove local vars
1097 memset (&pml, 0, sizeof (pml));
1098
1099 // convert origin and velocity to float values
1100 pml.origin[0] = pm->state.origin[0]*(1.0f/8.0f);
1101 pml.origin[1] = pm->state.origin[1]*(1.0f/8.0f);
1102 pml.origin[2] = pm->state.origin[2]*(1.0f/8.0f);
1103
1104 pml.velocity[0] = pm->state.velocity[0]*(1.0f/8.0f);
1105 pml.velocity[1] = pm->state.velocity[1]*(1.0f/8.0f);
1106 pml.velocity[2] = pm->state.velocity[2]*(1.0f/8.0f);
1107
1108 // save old org in case we get stuck
1109 Vec3Copy (pm->state.origin, pml.previousOrigin);
1110
1111 pml.frameTime = pm->cmd.msec * 0.001;
1112
1113 PM_ClampAngles ();
1114
1115 if (pm->state.pmType == PMT_SPECTATOR) {
1116 pml.frameTime *= pm->multiplier;
1117 PM_FlyMove (qFalse);
1118 PM_SnapPosition ();
1119 return;
1120 }
1121
1122 if (pm->state.pmType >= PMT_DEAD) {
1123 pm->cmd.forwardMove = 0;
1124 pm->cmd.sideMove = 0;
1125 pm->cmd.upMove = 0;
1126 }
1127
1128 if (pm->state.pmType == PMT_FREEZE)
1129 return; // no movement at all
1130
1131 // set mins, maxs, and viewHeight
1132 PM_CheckDuck ();
1133
1134 if (pm->snapInitial)
1135 PM_InitialSnapPosition ();
1136
1137 // set groundEntity, waterType, and waterLevel
1138 PM_CatagorizePosition ();
1139
1140 if (pm->state.pmType == PMT_DEAD)
1141 PM_DeadMove ();
1142
1143 PM_CheckSpecialMovement ();
1144
1145 // drop timing counter
1146 if (pm->state.pmTime) {
1147 int msec;
1148
1149 msec = pm->cmd.msec >> 3;
1150 if (!msec)
1151 msec = 1;
1152 if (msec >= pm->state.pmTime) {
1153 pm->state.pmFlags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_TELEPORT);
1154 pm->state.pmTime = 0;
1155 }
1156 else
1157 pm->state.pmTime -= msec;
1158 }
1159
1160 if (pm->state.pmFlags & PMF_TIME_TELEPORT) {
1161 // teleport pause stays exactly in place
1162 }
1163 else if (pm->state.pmFlags & PMF_TIME_WATERJUMP) {
1164 // waterjump has no control, but falls
1165 pml.velocity[2] -= pm->state.gravity * pml.frameTime;
1166 if (pml.velocity[2] < 0) {
1167 // cancel as soon as we are falling down again
1168 pm->state.pmFlags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_TELEPORT);
1169 pm->state.pmTime = 0;
1170 }
1171
1172 PM_StepSlideMove ();
1173 }
1174 else {
1175 PM_CheckJump ();
1176
1177 PM_Friction ();
1178
1179 if (pm->waterLevel >= 2) {
1180 PM_WaterMove ();
1181 }
1182 else {
1183 vec3_t angles;
1184
1185 Vec3Copy(pm->viewAngles, angles);
1186 if (angles[PITCH] > 180)
1187 angles[PITCH] = angles[PITCH] - 360;
1188 angles[PITCH] /= 3;
1189
1190 Angles_Vectors (angles, pml.forward, pml.right, pml.up);
1191
1192 PM_AirMove ();
1193 }
1194 }
1195
1196 // set groundEntity, waterType, and waterLevel for final spot
1197 PM_CatagorizePosition ();
1198
1199 PM_SnapPosition ();
1200 }
1201