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