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