1 //-----------------------------------------------------------------------------
2 // Client
3 //-----------------------------------------------------------------------------
4
5 #include "client.h"
6 #include "cake.h"
7 #include "commands.h"
8 #include "console.h"
9 #include "camera.h"
10 #include "timer.h"
11 #include "vars.h"
12 #include "math.h"
13 #include "world.h"
14 #include "sound.h"
15
16 void InitPlayerSounds(PlayerSounds *s);
17 void FreePlayerSounds(PlayerSounds *s);
18 int LoadPlayerSound(const char *path, const char *soundname);
19
Client(void)20 Client::Client(void)
21 {
22 life = 100;
23 armor = 100;
24
25 flying = true; // default value (currently true for debug purpose)
26 falling = false; // initialisation (updated in world.CheckCollision())
27 swimming = false;
28
29 active_client = true;
30 isCurrent = false;
31
32 fov = 90;
33 zoomlimit = 20;
34 max_speed = 300;
35 acceleration = 4096;
36 friction = 1024;
37 jump_height = 286;
38
39 pitchLimit = true; // pitch limit enabled by default
40
41 noclip = false;
42
43 VectorClear(velocity); // initialization
44 VectorSet(mins, -15, -15, -48);
45 VectorSet(maxs, 15, 15, 8);
46 step_size = 18; // default value
47 viewheight = mins[2];
48
49 VectorSet(extSpeed, 0, 0, 0); // initialization
50
51 nzoom = pzoom = 1;
52
53 progress = 1; // initialization - all interpolations are complete
54
55 color[0] = rand()%255;
56 color[1] = rand()%255;
57 color[2] = rand()%255;
58 color[3] = 64;
59
60 InitPlayerSounds(&sounds);
61 sprintf(name, "");
62 }
63
~Client(void)64 Client::~Client(void)
65 {
66 FreePlayerSounds(&sounds);
67 }
68
Init(int left,int top,int width,int height)69 void Client::Init(int left, int top, int width, int height)
70 {
71 cam.viewport_width = (float) width;
72 cam.viewport_height = (float) height;
73 cam.viewport_top = (float) top;
74 cam.viewport_left = (float) left;
75
76 if (cam.viewport_width < 0) cam.viewport_width = (float) gVars->IntForKey("v_width");
77 if (cam.viewport_height < 0) cam.viewport_height = (float) gVars->IntForKey("v_height");
78
79 if (cam.viewport_width == 0 || cam.viewport_height == 0) // there may be an error, use default res
80 {
81 gConsole->Insertln("^5WARNING: --- uh, uh, something is going wrong");
82
83 cam.viewport_width = 640;
84 cam.viewport_height = 480;
85 }
86
87 cmd.forwardmove = 0;
88 cmd.sidemove = 0;
89 cmd.upmove = 0;
90
91 noclip = false;
92
93 UpdateCam();
94 }
95
LoadPlayer(const char * playername)96 void Client::LoadPlayer(const char *playername)
97 {
98 if (!playername)
99 {
100 gConsole->Insertln("^5WARNING: LoadPlayer received empty player name. No player loaded.");
101 return;
102 }
103
104 // free current sounds
105 FreePlayerSounds(&sounds);
106
107 memset(name, '\0', MAX_PLAYERNAME_LENGTH*sizeof(char));
108 strcpy(name, playername);
109
110 // get gender (FIXME: get gender from player)
111 gender = rand()%3+1;
112
113 // load specific sounds
114 char soundpath[256] = { '\0' };
115 sprintf(soundpath, "sound/player/%s/", playername);
116
117 sounds.death1 = LoadPlayerSound(soundpath, "death1.wav");
118 sounds.death2 = LoadPlayerSound(soundpath, "death2.wav");
119 sounds.death3 = LoadPlayerSound(soundpath, "death3.wav");
120 sounds.drown = LoadPlayerSound(soundpath, "drown.wav");
121 sounds.fall1 = LoadPlayerSound(soundpath, "fall1.wav");
122 sounds.falling1 = LoadPlayerSound(soundpath, "falling1.wav");
123 sounds.gasp = LoadPlayerSound(soundpath, "gasp.wav");
124 sounds.jump1 = LoadPlayerSound(soundpath, "jump1.wav");
125 sounds.pain25_1 = LoadPlayerSound(soundpath, "pain25_1.wav");
126 sounds.pain50_1 = LoadPlayerSound(soundpath, "pain50_1.wav");
127 sounds.pain75_1 = LoadPlayerSound(soundpath, "pain75_1.wav");
128 sounds.pain100_1 = LoadPlayerSound(soundpath, "pain100_1.wav");
129 sounds.taunt = LoadPlayerSound(soundpath, "taunt.wav");
130
131 // load common sounds
132 sounds.fry = load3DSound("sound/player/fry.wav");
133 sounds.gibimp1 = load3DSound("sound/player/gibimp1.wav");
134 sounds.gibimp2 = load3DSound("sound/player/gibimp2.wav");
135 sounds.gibimp3 = load3DSound("sound/player/gibimp3.wav");
136 sounds.gibsplt1 = load3DSound("sound/player/gibsplt1.wav");
137 sounds.gurp1 = load3DSound("sound/player/gurp1.wav");
138 sounds.gurp2 = load3DSound("sound/player/gurp2.wav");
139 sounds.land1 = load3DSound("sound/player/land1.wav");
140 sounds.talk = load3DSound("sound/player/talk.wav");
141 sounds.watr_in = load3DSound("sound/player/watr_in.wav");
142 sounds.watr_out = load3DSound("sound/player/watr_out.wav");
143 sounds.watr_un = load3DSound("sound/player/watr_un.wav");
144 }
145
LoadPlayerSound(const char * path,const char * soundname)146 int LoadPlayerSound(const char *path, const char *soundname)
147 {
148 char soundpathname[256] = { '\0' };
149 sprintf(soundpathname, "%s%s", path, soundname);
150 return load3DSound(soundpathname);
151 }
152
InitPlayerSounds(PlayerSounds * s)153 void InitPlayerSounds(PlayerSounds *s)
154 {
155 s->death1 = s->death2 = s->death3 =
156 s->drown = s->fall1 = s->falling1 =
157 s->gasp = s->jump1 = s->taunt =
158 s->pain25_1 = s->pain50_1 = s->pain75_1 = s->pain100_1 =
159
160 s->fry = s->gibimp1 = s->gibimp2 = s->gibimp3 = s->gibsplt1 =
161 s->gurp1 = s->gurp2 = s->land1 = s->talk =
162 s->watr_in = s->watr_out = s->watr_un = -1;
163 }
164
FreePlayerSounds(PlayerSounds * s)165 void FreePlayerSounds(PlayerSounds *s)
166 {
167 if (s->death1 >= 0) freeSound(s->death1);
168 if (s->death2 >= 0) freeSound(s->death2);
169 if (s->death3 >= 0) freeSound(s->death3);
170 if (s->drown >= 0) freeSound(s->drown);
171 if (s->fall1 >= 0) freeSound(s->fall1);
172 if (s->falling1 >= 0) freeSound(s->falling1);
173 if (s->gasp >= 0) freeSound(s->gasp);
174 if (s->jump1 >= 0) freeSound(s->jump1);
175 if (s->pain25_1 >= 0) freeSound(s->pain25_1);
176 if (s->pain50_1 >= 0) freeSound(s->pain50_1);
177 if (s->pain75_1 >= 0) freeSound(s->pain75_1);
178 if (s->pain100_1 >= 0) freeSound(s->pain100_1);
179 if (s->taunt >= 0) freeSound(s->taunt);
180
181 if (s->fry >= 0) freeSound(s->fry);
182 if (s->gibimp1 >= 0) freeSound(s->gibimp1);
183 if (s->gibimp2 >= 0) freeSound(s->gibimp2);
184 if (s->gibimp3 >= 0) freeSound(s->gibimp3);
185 if (s->gibsplt1 >= 0) freeSound(s->gibsplt1);
186 if (s->gurp1 >= 0) freeSound(s->gurp1);
187 if (s->gurp2 >= 0) freeSound(s->gurp2);
188 if (s->land1 >= 0) freeSound(s->land1);
189 if (s->talk >= 0) freeSound(s->talk);
190 if (s->watr_in >= 0) freeSound(s->watr_in);
191 if (s->watr_out >= 0) freeSound(s->watr_out);
192 if (s->watr_un >= 0) freeSound(s->watr_un);
193 InitPlayerSounds(s);
194 }
195
ResetMovement(void)196 void Client::ResetMovement(void)
197 {
198 falling = false; // initialisation (updated in world.CheckCollision())
199 swimming = false;
200
201 // Interpolation stuff
202 nzoom = pzoom = 1;
203 progress = 1; // initialization - all interpolations are complete
204
205 VectorClear(extSpeed);
206 VectorClear(velocity);
207
208 cmd.forwardmove = 0;
209 cmd.sidemove = 0;
210 cmd.upmove = 0;
211 cmd.move_mouse_x = 0;
212 cmd.move_mouse_y = 0;
213 }
214
UpdateCam(void)215 void Client::UpdateCam(void)
216 {
217 float real_width = (float) cam.viewport_width*cam.viewport_size;
218 float real_height = (float) cam.viewport_height*cam.viewport_size;
219
220 cam.Init(fov, real_width, real_height);
221 }
222
223 /**
224 * @todo Consider water -> other friction and other acceleration
225 * @todo Consider jumps -> cannot change direction while jumping
226 */
227 #define FALLING_FRICTION_REDUCTION 8.f // reduction of friction if falling
228 #define FALLING_ACCELERATION_REDUCTION 4.f // reduction of acceleration if falling
229 #define SWIMMING_FRICTION_REDUCTION 1.4f // reduction of friction if swimming
230 #define SWIMMING_ACCELERATION_REDUCTION 1.2f // reduction of acceleration if swimming
Update(Q3BSP * bsp,World * wld)231 void Client::Update(Q3BSP *bsp, World *wld)
232 {
233 vec3_t temp, dir = { 0.0f, 0.0f, 0.0f }, versor = { 0.0f, 0.0f, 0.0f };
234
235 curr_bsp = bsp; // update current bsp
236 curr_world = wld; // update current world
237
238 swimming = cam.inWater;
239
240 if (flying || swimming || noclip) versor[1] += cmd.upmove;
241 versor[0] += cmd.sidemove;
242 versor[2] -= cmd.forwardmove;
243
244
245 // Update rotation
246 cam.rot[PITCH] += cmd.move_mouse_y;
247 cam.rot[YAW] += cmd.move_mouse_x;
248
249
250 // Get the direction vector using camera rotation
251 if (flying || swimming || noclip)
252 {
253 cam.UpdateRotation(); // we need the rotation matrix
254 MultVect3x3(cam.rotation, versor, dir);
255 VectorCopy(cam.forward, forward);
256 VectorCopy(cam.right, right);
257 VectorCopy(cam.up, up);
258 }
259 else
260 {
261 float rad_yaw = DEG2RAD(cam.rot[YAW]);
262 dir[0] = versor[0]*FastCos(rad_yaw) + versor[2]*FastSin(rad_yaw);
263 dir[1] = versor[0]*FastSin(rad_yaw) - versor[2]*FastCos(rad_yaw);
264
265 forward[0] = cam.forward[0];
266 forward[1] = cam.forward[1];
267 forward[2] = 0;
268
269 right[0] = cam.right[0];
270 right[1] = cam.right[1];
271 right[2] = 0;
272
273 VectorSet(up, 0, 0, 1);
274
275 VectorNormalize(forward);
276 VectorNormalize(right);
277 }
278
279 // Acceleration
280 // TODO: Manage acceleration and friction for water and air movements
281 if (VectorLength(versor))
282 {
283 float r_acceleration = (float)Timer::frametime * acceleration;
284 if (swimming) r_acceleration /= SWIMMING_ACCELERATION_REDUCTION;
285 else if (falling) r_acceleration /= FALLING_ACCELERATION_REDUCTION;
286
287 // Apply acceleration
288 velocity[0] += dir[0]*r_acceleration;
289 velocity[1] += dir[1]*r_acceleration;
290 velocity[2] += dir[2]*r_acceleration;
291 }
292
293 VectorCopy(velocity, temp);
294 float vel = VectorNormalize(temp);
295
296 // Friction
297 float r_friction = (float)Timer::frametime * friction;
298 if (swimming) r_friction /= SWIMMING_FRICTION_REDUCTION;
299 else if (falling) r_friction /= FALLING_FRICTION_REDUCTION;
300
301 velocity[0] -= temp[0]*r_friction;
302 velocity[1] -= temp[1]*r_friction;
303 velocity[2] -= temp[2]*r_friction;
304
305 if ((temp[0] > 0 && velocity[0] < 0) || (temp[0] < 0 && velocity[0] > 0)) velocity[0] = 0;
306 if ((temp[1] > 0 && velocity[1] < 0) || (temp[1] < 0 && velocity[1] > 0)) velocity[1] = 0;
307 if ((temp[2] > 0 && velocity[2] < 0) || (temp[2] < 0 && velocity[2] > 0)) velocity[2] = 0;
308
309
310 // Speed
311 if (vel > max_speed)
312 {
313 float ivel = 1/vel*max_speed;
314 VectorScale(velocity, ivel, velocity);
315 }
316
317 // dont go out of bounds
318 if (pitchLimit)
319 {
320 if (cam.rot[PITCH] < 0.0f) cam.rot[PITCH] = 0.0f;
321 else if (cam.rot[PITCH] > 180.0f) cam.rot[PITCH] = 180.0f;
322 }
323
324 // Reinit movement for next frame
325 cmd.forwardmove = 0;
326 cmd.sidemove = 0;
327 cmd.upmove = 0;
328 cmd.move_mouse_x = 0;
329 cmd.move_mouse_y = 0;
330
331
332 // Position update
333 if (noclip)
334 {
335 VectorMA(cam.pos, (float) Timer::frametime, velocity, cam.pos);
336 }
337 else
338 {
339 vec3_t pos;
340 VectorMA(cam.pos, (float) Timer::frametime, velocity, pos);
341 CheckCollision(pos);
342 }
343
344 // subwater time update
345 // FIXME: should it be done here ?
346 if (life > 0 &&
347 swimming &&
348 diveStart + 10 < Timer::fTime &&
349 Timer::fTime - lastgurptime > 1)
350 {
351 lastgurptime = Timer::fTime;
352 Pain((int) (Timer::fTime-diveStart));
353 Gurp(gender);
354
355 if (life <= 0)
356 {
357 // restore life
358 life = 100;
359 diveStart = Timer::fTime;
360 }
361 }
362 }
363
SetAngle(float p,float y,float r)364 void Client::SetAngle(float p, float y, float r)
365 {
366 VectorSet(cam.rot, p, y, r);
367 }
368
SetAngle(vec3_t rot)369 void Client::SetAngle(vec3_t rot)
370 {
371 VectorCopy(rot, cam.rot);
372 }
373
SetPos(float x,float y,float z)374 void Client::SetPos(float x, float y, float z)
375 {
376 VectorSet(cam.pos, x, y, z-viewheight);
377 }
378
SetPos(vec3_t pos)379 void Client::SetPos(vec3_t pos)
380 {
381 VectorCopy(pos, cam.pos);
382 cam.pos[2] -= viewheight;
383 }
384
AddAngle(float p,float y,float r)385 void Client::AddAngle(float p, float y, float r)
386 {
387 cam.rot[PITCH] += p;
388 cam.rot[YAW] += y;
389 cam.rot[ROLL] += r;
390 }
391
AddPos(float x,float y,float z)392 void Client::AddPos(float x, float y, float z)
393 {
394 cam.pos[0] += x;
395 cam.pos[1] += y;
396 cam.pos[2] += z;
397 }
398
MoveForward(float val)399 void Client::MoveForward(float val)
400 {
401 cmd.forwardmove += val;
402 }
403
MoveBackward(float val)404 void Client::MoveBackward(float val)
405 {
406 cmd.forwardmove -= val;
407 }
408
MoveRight(float val)409 void Client::MoveRight(float val)
410 {
411 cmd.sidemove += val;
412 }
413
MoveLeft(float val)414 void Client::MoveLeft(float val)
415 {
416 cmd.sidemove -= val;
417 }
418
MoveUp(float val)419 void Client::MoveUp(float val)
420 {
421 cmd.upmove += val;
422 }
423
MoveDown(float val)424 void Client::MoveDown(float val)
425 {
426 cmd.upmove -= val;
427 }
428
MoveMouseX(float val)429 void Client::MoveMouseX(float val)
430 {
431 // the rotation is proportionnal to camera fov
432 cmd.move_mouse_x = val*cam.fov/M_TWO_PI;
433 }
434
MoveMouseY(float val)435 void Client::MoveMouseY(float val)
436 {
437 // the rotation is proportionnal to camera fov
438 cmd.move_mouse_y = val*cam.fov/M_TWO_PI;
439 }
440
MoveMouseXY(float xval,float yval)441 void Client::MoveMouseXY(float xval, float yval)
442 {
443 // the rotation is proportionnal to camera fov
444 cmd.move_mouse_x = xval*cam.fov/M_TWO_PI;
445 cmd.move_mouse_y = yval*cam.fov/M_TWO_PI;
446 }
447
Jump(void)448 void Client::Jump(void)
449 {
450 // No jump when already jumping (or falling)
451 if (!flying && !falling && !noclip)
452 {
453 extSpeed[2] = jump_height;
454 if (!flying) falling = true;
455
456 // play jump sound
457 playSound(sounds.jump1, cam.pos);
458 }
459 }
460
Land(void)461 void Client::Land(void)
462 {
463 playSound(sounds.land1, cam.pos);
464 }
465
Fall(void)466 void Client::Fall(void)
467 {
468 playSound(sounds.fall1, cam.pos);
469 }
470
Falling(void)471 void Client::Falling(void)
472 {
473 playSound(sounds.falling1, cam.pos);
474 }
475
Die(int depthnum)476 void Client::Die(int depthnum)
477 {
478 switch (depthnum)
479 {
480 case 1:
481 playSound(sounds.death1, cam.pos);
482 break;
483 case 2:
484 playSound(sounds.death2, cam.pos);
485 break;
486 case 3:
487 default:
488 playSound(sounds.death3, cam.pos);
489 break;
490 }
491 life = 0;
492 }
493
Gasp(void)494 void Client::Gasp(void)
495 {
496 playSound(sounds.gasp, cam.pos);
497 }
498
Drown(void)499 void Client::Drown(void)
500 {
501 playSound(sounds.drown, cam.pos);
502 }
503
Pain(int amount)504 void Client::Pain(int amount)
505 {
506 life -= amount;
507 gConsole->Insertln("client life: %d", life);
508
509 if (life <= 0) Die(rand()%3+1);
510 else if (life > 0 && life < 25) playSound(sounds.pain25_1, cam.pos);
511 else if (life >= 25 && life < 50) playSound(sounds.pain50_1, cam.pos);
512 else if (life >= 50 && life < 75) playSound(sounds.pain75_1, cam.pos);
513 else playSound(sounds.pain100_1, cam.pos);
514 }
515
Taunt(void)516 void Client::Taunt(void)
517 {
518 playSound(sounds.taunt, cam.pos);
519 }
520
Fry(void)521 void Client::Fry(void)
522 {
523 playSound(sounds.fry, cam.pos);
524 Pain(25);
525 }
526
Gibimp(int type)527 void Client::Gibimp(int type)
528 {
529 switch (type)
530 {
531 case 1:
532 playSound(sounds.gibimp1, cam.pos);
533 break;
534 case 2:
535 playSound(sounds.gibimp2, cam.pos);
536 break;
537 case 3:
538 playSound(sounds.gibimp3, cam.pos);
539 break;
540 case 0:
541 default:
542 playSound(sounds.gibsplt1, cam.pos);
543 break;
544 }
545 }
546
Gurp(int type)547 void Client::Gurp(int type)
548 {
549 switch (type)
550 {
551 case 2:
552 playSound(sounds.gurp2, cam.pos);
553 break;
554 case 1:
555 default:
556 playSound(sounds.gurp1, cam.pos);
557 break;
558 }
559 }
560
Talk(void)561 void Client::Talk(void)
562 {
563 playSound(sounds.talk, cam.pos);
564 }
565
Water(int type)566 void Client::Water(int type)
567 {
568 /**< type -> 0 = in, 1 = out, 2 = un */
569 switch (type)
570 {
571 case 1:
572 playSound(sounds.watr_out, cam.pos);
573 break;
574 case 2:
575 playSound(sounds.watr_un, cam.pos);
576 break;
577 case 0:
578 default:
579 playSound(sounds.watr_in, cam.pos);
580 diveStart = Timer::fTime;
581 lastgurptime = diveStart;
582 break;
583 }
584 }
585
JumpTo(vec3_t dest)586 bool Client::JumpTo(vec3_t dest)
587 {
588 if (!flying && !falling && !noclip)
589 {
590 VectorClear(velocity);
591
592 /*
593 ** The bumper target is the top of the trajectory curve:
594 **
595 ** / target
596 ** start speed / __--X--__
597 ** /_-""_-" ""-_
598 ** /" _-' "_
599 ** start X_-'-----------------X end
600 ** --------. <- range -> .---------
601 ** ________| |_________
602 **
603 **
604 ** So we'll have to solve the motion equation:
605 **
606 ** x(t) = x0 + Vx.t
607 ** y(t) = y0 + Vy.t
608 ** z(t) = z0 + Vz.t - 1/2.g.t^2
609 **
610 ** when the motion reaches its end point, we're on z = z0:
611 ** so the motion range is:
612 **
613 ** z(t) = z0 + Vz.t_end - 1/2.g.t_end^2
614 ** z(t) = z0;
615 **
616 ** Vz.t_end - 1/2.g.t_end^2 = 0
617 ** this equation has two solutions:
618 ** t_end0 = 0
619 ** t_end1 = 2.Vz / g
620 **
621 ** we're only interested in the second one, and the maximum
622 ** motion height is achieved when the horizontal distance
623 ** equals half the range:
624 **
625 ** t_max = Vz / g
626 **
627 ** We just have to plug that in the equations:
628 **
629 ** x_target = x0 + Vx.Vz / g
630 ** y_target = y0 + Vy.Vz / g
631 ** z_target = z0 + Vz.Vz / g - 1/2.g.(Vz / g)^2
632 **
633 ** so we finally get:
634 **
635 ** Vz = sqrt(2.g.(z_target - z0))
636 ** Vx = (x_target - x0).g / Vz
637 ** Vy = (y_target - y0).g / Vz
638 **
639 ** and we have our accelerator pad speed vector: {Vx, Vy, Vz}
640 **
641 */
642
643 extSpeed[2] = sqrtf(2*curr_world->gravity*(dest[2]-cam.pos[2]-viewheight));
644 extSpeed[0] = (dest[0] - cam.pos[0])*curr_world->gravity / extSpeed[2];
645 extSpeed[1] = (dest[1] - cam.pos[1])*curr_world->gravity / extSpeed[2];
646
647 swimming = false;
648 falling = true;
649
650 progress = 1; // initialization - all interpolations are complete
651
652 // play jump sound
653 playSound(sounds.jump1, cam.pos);
654
655 return true;
656 }
657 else return false;
658 }
659
TeleportTo(vec3_t dest,float angle,float startwalk)660 bool Client::TeleportTo(vec3_t dest, float angle, float startwalk)
661 {
662 cam.pos[0] = dest[0];
663 cam.pos[1] = dest[1];
664 cam.pos[2] = dest[2]-viewheight;
665
666 // Reset movement
667 falling = false; // initialisation (updated in world.CheckCollision())
668 swimming = false;
669
670 // Interpolation stuff
671 progress = 1; // initialization - all interpolations are complete
672
673 VectorClear(extSpeed);
674 VectorClear(velocity);
675
676 cam.rot[PITCH] = 90;
677 cam.rot[YAW] = angle-90;
678 cam.rot[ROLL] = 0;
679
680 MoveForward(startwalk); // make the client moving forward
681
682 return true;
683 }
684
Zoom(float val)685 void Client::Zoom(float val)
686 {
687 if (val < 0 && !nzoom || val > 0 && !pzoom) return;
688
689 float camzoom = cam.GetFov();
690 float zoomval = val*(float)Timer::frametime;
691
692 if (zoomval > 0)
693 {
694 if (camzoom+zoomval < fov)
695 {
696 cam.SetFov(camzoom+zoomval);
697 pzoom = true; // there is variation for val > 0
698 }
699 else
700 {
701 cam.SetFov(fov);
702 pzoom = false; // there is no variation for val > 0
703 }
704 nzoom = true;
705 }
706 else if (zoomval < 0)
707 {
708 if (camzoom+zoomval > zoomlimit)
709 {
710 cam.SetFov(camzoom+zoomval);
711 nzoom = true; // there is variation for val < 0
712 }
713 else
714 {
715 cam.SetFov(zoomlimit);
716 nzoom = false; // there is no variation for val < 0
717 }
718 pzoom = true;
719 }
720 }
721
TouchBounds(bboxf_t bbox)722 int Client::TouchBounds(bboxf_t bbox)
723 {
724 if (noclip) return false;
725
726 vec3_t cmins, cmaxs;
727 VectorAdd(mins, cam.pos, cmins);
728 VectorAdd(maxs, cam.pos, cmaxs);
729
730 return BoundsIntersect(bbox, cmins, cmaxs);
731 }
732
733 #define MIN_STEP_NORMAL 0.7f // can't step up onto very steep slopes
734 #define DELTA_DOWN 1
735 #define INTERPOLATION_SPEED 0.125f // Speed used for interpolations (proportional to height difference)
CheckCollision(vec3_t targetpos)736 void Client::CheckCollision(vec3_t targetpos)
737 {
738 trace_t trace;
739 vec3_t depl, pos, endPoint = { 0, 0, 0 };
740 float time_frame = (float) Timer::frametime * 100;
741 bool jumping = (!flying && !swimming && extSpeed[2] > 0);
742
743 VectorCopy(cam.pos, pos);
744 VectorSub(targetpos, pos, depl);
745
746 if (depl[0] || depl[1] || depl[2])
747 {
748 float stepdiff = 0;
749 if (progress < 1)
750 {
751 stepdiff = progress*interpolation;
752 depl[2] -= stepdiff;
753 }
754 StepSlideMove(depl);
755 VectorAdd(pos, depl, cam.pos);
756
757 endPoint[2] = cam.pos[2] - DELTA_DOWN;
758 trace = curr_bsp->CheckMove(cam.pos, mins, maxs, endPoint);
759
760 // Step climbing movement
761 // In step case, plane normal is not perpendicular to displacement
762 // => dot product not null
763 if (!flying && depl[2]+stepdiff > 1 && depl[2] < 20 &&
764 DotProduct(trace.plane.normal, depl) != 0)
765 {
766 if (progress < 1)
767 {
768 // another interpolation is already in progress
769 startheight += (progress * interpolation);
770 interpolation = depl[2] + pos[2] - startheight + stepdiff;
771 }
772 else
773 {
774 // start new interpolation
775 startheight = pos[2];
776 interpolation = depl[2];
777 }
778
779 progress = 0; // start interpolation
780 }
781 }
782 else
783 {
784 // create an empty trace
785 trace.contents = 0;
786 trace.fraction = 1;
787 trace.shader = -1;
788 }
789
790 // Check triggered evenments
791 // if (trace.shader >= 0)
792 // gConsole->Insertln("^6trace.shader.content: %d | trace.shader.surface: %d",
793 // shaders.GetShader(trace.shader)->content_flags,shaders.GetShader(trace.shader)->surface_flags);
794 // gConsole->Insertln("^5trace.contents: %d", trace.contents );
795
796 // Check if falling, jumping or anyth-ing ;)
797 endPoint[2] = cam.pos[2] - DELTA_DOWN;
798 trace = curr_bsp->CheckMove(cam.pos, mins, maxs, endPoint);
799 falling = (!flying && trace.fraction >= 0.01);
800
801 if (progress < 1)
802 {
803 // Process interpolation (proportional to frametime)
804 progress += (INTERPOLATION_SPEED * time_frame);
805
806 if (progress > 1) progress = 1;
807
808 cam.pos[2] = startheight + progress * interpolation;
809 }
810 else if (falling)
811 {
812 // if the client is falling (actually if he is not on
813 // the ground, for instance when jumping), only add gravity
814 extSpeed[2] -= curr_world->gravity * (float) Timer::frametime;
815
816 progress = 1; // reset interpolation
817
818 // clamp vertical speed
819 if (extSpeed[2] < -curr_world->maxvspeed) extSpeed[2] = -curr_world->maxvspeed;
820 }
821 /* else if (trace.plane.normal[2] < MIN_STEP_NORMAL)
822 {
823 #if 0
824 VectorNormalize(trace.normal);
825
826 // Slide with speed relative to ground slope
827 vec3_t slide = { 0, 0, -(curr_world->gravity * (float) Timer::frametime) * (1 - trace.normal[2] / MIN_STEP_NORMAL) };
828
829 // Remaining to move
830 vec3_t remain;
831 VectorMA(slide, -trace.fraction, slide, remain);
832
833 float a = DotProduct(remain, trace.normal);
834 VectorScale(trace.normal, a, slide);
835 VectorSub(remain, slide, slide);
836
837 VectorAdd(cam.pos, slide, endPoint);
838 trace = curr_bsp->checkMove(cam.pos, mins, maxs, endPoint);
839
840 if (trace.fraction >= 0.01) VectorCopy(trace.end, cam.pos);
841 #else
842 extSpeed[2] -= (curr_world->gravity * (float) Timer::frametime) * (1 - trace.plane.normal[2] / MIN_STEP_NORMAL);
843 #endif
844 }*/
845
846 if (!jumping && !falling && progress >= 1)
847 { // -> if progress < 1 is treated below
848 VectorClear(extSpeed);
849 }
850
851 // jumping or falling
852 if (extSpeed[2] && !flying)
853 {
854 vec3_t sp;
855 VectorScale(extSpeed, (float)Timer::frametime, sp);
856 VectorCopy(sp, depl);
857 VectorCopy(cam.pos, pos);
858
859 trace = StepSlideMove(depl);
860
861 progress = 1; // reset interpolation
862
863 VectorAdd(pos, depl, cam.pos);
864
865 if (extSpeed[2] > 0 && trace.fraction < 1)
866 VectorClear(extSpeed);
867 }
868 }
869
StepSlideMove(vec3_t vel)870 trace_t Client::StepSlideMove(vec3_t vel)
871 {
872 // Dtag method
873 trace_t trace;
874 vec3_t start_o, start_v;
875 vec3_t down_o, down_v;
876 vec3_t up, down;
877
878 VectorCopy(cam.pos, start_o); // backup of start position
879 VectorCopy(vel, start_v); // backup of wished end position
880
881 VectorCopy(cam.pos, down_o);
882 VectorCopy(vel, down_v);
883
884 StepSlideMove_(down_o, down_v, mins, maxs);
885
886 up[0] = start_o[0];
887 up[1] = start_o[1];
888 up[2] = start_o[2] + step_size;
889
890 // check if up pos is in a solid
891 trace = curr_bsp->CheckMove(up, mins, maxs, up);
892 if (trace.allSolid && progress >= 1)
893 {
894 VectorCopy(down_o, cam.pos);
895 VectorCopy(down_v, vel);
896 return trace; // can't step up
897 }
898
899 // Try sliding above
900 VectorCopy(up, cam.pos);
901 VectorCopy(start_v, vel);
902 StepSlideMove_(cam.pos, vel, mins, maxs);
903
904 // push down the final amount
905 down[0] = cam.pos[0];
906 down[1] = cam.pos[1];
907 down[2] = cam.pos[2] - step_size;
908 trace = curr_bsp->CheckMove(cam.pos, mins, maxs, down);
909 if (!trace.allSolid)
910 {
911 VectorCopy(trace.end, cam.pos);
912 }
913
914 #if 0
915 VectorSub(cam.pos, up, delta);
916 up_dist = DotProduct(delta, start_v);
917
918 VectorSub(down_o, start_o, delta);
919 down_dist = DotProduct(delta, start_v);
920 #else
921 VectorCopy(cam.pos, up);
922
923 // Decide which one went farther
924 float down_dist = (down_o[0] - start_o[0])*(down_o[0] - start_o[0]) +
925 (down_o[1] - start_o[1])*(down_o[1] - start_o[1]);
926 float up_dist = (up[0] - start_o[0])*(up[0] - start_o[0]) +
927 (up[1] - start_o[1])*(up[1] - start_o[1]);
928 #endif
929
930 if (down_dist > up_dist || trace.plane.normal[2] < MIN_STEP_NORMAL)
931 {
932 VectorCopy(down_v, vel);
933 VectorCopy(down_o, cam.pos);
934 return trace;
935 }
936
937 VectorSub(up, start_o, vel);
938 return trace;
939 }
940
941 #define STOP_EPSILON 0.1
ClipVelocity(vec3_t in,vec3_t normal,vec3_t out,float overbounce)942 void Client::ClipVelocity(vec3_t in, vec3_t normal, vec3_t out, float overbounce)
943 {
944 float change, backoff = DotProduct(in, normal) * overbounce;
945
946 change = normal[0] * backoff;
947 out[0] = in[0] - change;
948 if (out[0] > -STOP_EPSILON && out[0] < STOP_EPSILON) out[0] = 0;
949 change = normal[1] * backoff;
950 out[1] = in[1] - change;
951 if (out[1] > -STOP_EPSILON && out[1] < STOP_EPSILON) out[1] = 0;
952 change = normal[2] * backoff;
953 out[2] = in[2] - change;
954 if (out[2] > -STOP_EPSILON && out[2] < STOP_EPSILON) out[2] = 0;
955 }
956
957 #define MAX_CLIP_PLANES 5
StepSlideMove_(vec3_t pos,vec3_t vel,vec3_t mins,vec3_t maxs)958 void Client::StepSlideMove_(vec3_t pos, vec3_t vel, vec3_t mins, vec3_t maxs)
959 {
960 #if 1
961 // Dtag method
962 trace_t trace;
963 trace.fraction = 0;
964
965 vec3_t primalVel, end;
966 VectorCopy(vel, primalVel);
967
968 vec3_t planes[MAX_CLIP_PLANES];
969
970 int i, j, numbumps = 4;
971 float d, time_left = 1;
972
973 vec3_t dir;
974
975 int numplanes = 0;
976 for (int index = 0; index < numbumps; ++index)
977 {
978 time_left -= time_left * trace.fraction;
979 VectorMA(pos, time_left, vel, end);
980
981 trace = curr_bsp->CheckMove(pos, mins, maxs, end);
982
983 if (trace.allSolid)
984 { // entity is trapped in another solid
985 vel[2] = 0; // don't build up falling damage
986 break;
987 }
988
989 if (trace.fraction > 0)
990 { // actually covered some distance
991 VectorCopy(trace.end, pos);
992 numplanes = 0;
993 }
994
995 if (trace.fraction == 1) break; // moved the entire distance
996
997 // slide along this plane
998 if (numplanes >= MAX_CLIP_PLANES)
999 { // this shouldn't really happen
1000 VectorClear(vel);
1001 break;
1002 }
1003
1004 VectorCopy(trace.plane.normal, planes[numplanes]);
1005 ++numplanes;
1006
1007 #if 0
1008 float rub;
1009 // Modify velocity so it parallels all of the clip planes
1010 if (numplanes == 1)
1011 { // go along this plane
1012 VectorCopy(vel, dir);
1013 VectorNormalize(dir);
1014 rub = 1.0 + .05 * DotProduct(dir, planes[0]);
1015
1016 // slide along the plane
1017 clipVelocity(vel, planes[0], vel, 10.1f);
1018
1019 // rub some extra speed off on xy axis
1020 // not on Z, or you can scrub down walls
1021 VectorScale(vel, rub, vel);
1022 }
1023 else if (numplanes == 2)
1024 { // go along the crease
1025 VectorCopy(vel, dir);
1026 VectorNormalize(dir);
1027 rub = 1.0 + 0.5 * DotProduct(dir, planes[0]);
1028
1029 // slide along the plane
1030 CrossProduct(planes[0], planes[1], dir);
1031 d = DotProduct(dir, vel);
1032 VectorScale(dir, d, vel);
1033
1034 // rub some extra speed off
1035 VectorScale(vel, rub, vel);
1036 }
1037 else
1038 {
1039 VectorClear(vel);
1040 break;
1041 }
1042 #else
1043 // Modify original velocity so it parallels all of the clip planes
1044 for (i = 0; i < numplanes; ++i)
1045 {
1046 ClipVelocity(vel, planes[i], vel, 1.01f);
1047 for (j = 0; j < numplanes; ++j)
1048 if (j != i)
1049 {
1050 if (DotProduct(vel, planes[j]) < 0) break;
1051 }
1052 if (j == numplanes) break;
1053 }
1054
1055 if (i != numplanes)
1056 {
1057 // Go along this plane
1058 }
1059 else
1060 {
1061 // Go along the crease
1062 if (numplanes != 2)
1063 {
1064 VectorClear(vel);
1065 break;
1066 }
1067
1068 CrossProduct(planes[0], planes[1], dir);
1069 d = DotProduct(dir, vel);
1070 VectorScale(dir, d, vel);
1071 }
1072 #endif
1073
1074 // If velocity is against the original velocity, stop dead
1075 // to avoid tiny occilations in slopping corners
1076 if (DotProduct(vel, primalVel) <= 0)
1077 {
1078 VectorClear(vel);
1079 break;
1080 }
1081 }
1082 #else
1083 // Q2 method
1084 trace_t trace;
1085 trace.fraction = 0;
1086
1087 vec3_t primalVel, end;
1088 VectorCopy(vel, primalVel);
1089
1090 vec3_t planes[MAX_CLIP_PLANES];
1091
1092 int i, j, numbumps = 4;
1093 float d, time_left = (float) Timer::frametime;
1094
1095 vec3_t dir;
1096
1097 int numplanes = 0;
1098 for (int index = 0; index < numbumps; ++index)
1099 {
1100 VectorMA(pos, time_left, vel, end);
1101
1102 trace = curr_bsp->checkMove(pos, mins, maxs, end);
1103
1104 if (trace.allSolid)
1105 { // entity is trapped in another solid
1106 vel[2] = 0; // don't build up falling damage
1107 break;
1108 }
1109
1110 if (trace.fraction > 0)
1111 { // actually covered some distance
1112 VectorCopy(trace.end, pos);
1113 numplanes = 0;
1114 }
1115
1116 if (trace.fraction == 1)
1117 break; // moved the entire distance
1118
1119 time_left -= time_left * trace.fraction;
1120
1121 // slide along this plane
1122 if (numplanes >= MAX_CLIP_PLANES)
1123 { // this shouldn't really happen
1124 VectorClear(vel);
1125 break;
1126 }
1127
1128 VectorCopy(trace.plane.normal, planes[numplanes]);
1129 ++numplanes;
1130
1131 #if 0
1132 float rub;
1133 // Modify velocity so it parallels all of the clip planes
1134 if (numplanes == 1)
1135 { // go along this plane
1136 VectorCopy(vel, dir);
1137 VectorNormalize(dir);
1138 rub = 1.0 + .05 * DotProduct(dir, planes[0]);
1139
1140 // slide along the plane
1141 clipVelocity(vel, planes[0], vel, 10.1f);
1142
1143 // rub some extra speed off on xy axis
1144 // not on Z, or you can scrub down walls
1145 VectorScale(vel, rub, vel);
1146 }
1147 else if (numplanes == 2)
1148 { // go along the crease
1149 VectorCopy(vel, dir);
1150 VectorNormalize(dir);
1151 rub = 1.0 + 0.5 * DotProduct(dir, planes[0]);
1152
1153 // slide along the plane
1154 CrossProduct(planes[0], planes[1], dir);
1155 d = DotProduct(dir, vel);
1156 VectorScale(dir, d, vel);
1157
1158 // rub some extra speed off
1159 VectorScale(vel, rub, vel);
1160 }
1161 else
1162 {
1163 VectorClear(vel);
1164 break;
1165 }
1166 #else
1167 // Modify original velocity so it parallels all of the clip planes
1168 for (i = 0; i < numplanes; ++i)
1169 {
1170 clipVelocity(vel, planes[i], vel, 1.01f);
1171 for (j = 0; j < numplanes; ++j)
1172 if (j != i)
1173 {
1174 if (DotProduct(vel, planes[j]) < 0)
1175 break; // not ok
1176 }
1177 if (j == numplanes)
1178 break;
1179 }
1180
1181 if (i != numplanes)
1182 { // go along this plane
1183 }
1184 else
1185 { // go along the crease
1186 if (numplanes != 2)
1187 {
1188 VectorClear(vel);
1189 break;
1190 }
1191
1192 CrossProduct(planes[0], planes[1], dir);
1193 d = DotProduct(dir, vel);
1194 VectorScale(dir, d, vel);
1195 }
1196 #endif
1197
1198 // If velocity is against the original velocity, stop dead
1199 // to avoid tiny occilations in slopping corners
1200 if (DotProduct(vel, primalVel) <= 0)
1201 {
1202 VectorClear(vel);
1203 break;
1204 }
1205 }
1206 #endif
1207 }
1208