]> icculus.org git repositories - divverent/nexuiz.git/blob - qcsrc/gamec/bot_phys.c
added cl_movement_* defaults and some other stuff
[divverent/nexuiz.git] / qcsrc / gamec / bot_phys.c
1 /***********************************************
2 *                                              *
3 *                FrikBot Physics               *
4 *        The near-perfect emulation of         *
5 *                Client movement               *
6 *                                              *
7 *         Special Thanks to: Asdf, Frog        *
8 *             Alan "Strider" Kivlin            *
9 *                                              *
10 *                                              *
11 ***********************************************/
12
13 /*
14
15 This program is in the Public Domain. My crack legal
16 team would like to add:
17
18 RYAN "FRIKAC" SMITH IS PROVIDING THIS SOFTWARE "AS IS"
19 AND MAKES NO WARRANTY, EXPRESS OR IMPLIED, AS TO THE
20 ACCURACY, CAPABILITY, EFFICIENCY, MERCHANTABILITY, OR
21 FUNCTIONING OF THIS SOFTWARE AND/OR DOCUMENTATION. IN
22 NO EVENT WILL RYAN "FRIKAC" SMITH BE LIABLE FOR ANY
23 GENERAL, CONSEQUENTIAL, INDIRECT, INCIDENTAL,
24 EXEMPLARY, OR SPECIAL DAMAGES, EVEN IF RYAN "FRIKAC"
25 SMITH HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
26 DAMAGES, IRRESPECTIVE OF THE CAUSE OF SUCH DAMAGES.
27
28 You accept this software on the condition that you
29 indemnify and hold harmless Ryan "FrikaC" Smith from
30 any and all liability or damages to third parties,
31 including attorney fees, court costs, and other
32 related costs and expenses, arising out of your use
33 of this software irrespective of the cause of said
34 liability.
35
36 The export from the United States or the subsequent
37 reexport of this software is subject to compliance
38 with United States export control and munitions
39 control restrictions. You agree that in the event you
40 seek to export this software, you assume full
41 responsibility for obtaining all necessary export
42 licenses and approvals and for assuring compliance
43 with applicable reexport restrictions.
44
45 Any reproduction of this software must contain
46 this notice in its entirety.
47
48 */
49
50 /*
51 =========================================
52
53 Stuff mimicking cl_input.c code
54
55 =========================================
56 */
57 float(float key) CL_KeyState =
58 {
59         return ((self.keys & key) > 0);
60 };
61
62 void() CL_KeyMove = // CL_BaseMove + CL_AdjustAngles
63 {
64         local float anglespeed;
65         local vector view;
66         if (self.keys != self.oldkeys)
67         {
68                 self.movement = '0 0 0';
69                 self.movement_y = self.movement_y + (350 * CL_KeyState(KEY_MOVERIGHT));
70                 // 350 is the default cl_sidespeed
71                 self.movement_y = self.movement_y - (350 * CL_KeyState(KEY_MOVELEFT));
72                 // 350 is the default cl_sidespeed
73                 self.movement_x = self.movement_x + (200 * CL_KeyState(KEY_MOVEFORWARD));
74                 // 200 is the default cl_forwardspeed
75                 self.movement_x = self.movement_x - (200 * CL_KeyState(KEY_MOVEBACK));
76                 // 200 is the default cl_backspeed
77                 self.movement_z = self.movement_z + (200 * CL_KeyState(KEY_MOVEUP));
78                 // 200 is the default cl_upspeed
79                 self.movement_z = self.movement_z - (200 * CL_KeyState(KEY_MOVEDOWN));
80                 // 200 is the default cl_upspeed
81                 if (!self.b_aiflags & AI_PRECISION)
82                         self.movement = self.movement * 2;
83                 // 2 is the default cl_movespeedkey & bot always has +speed
84         }
85         self.oldkeys = self.keys;
86
87         if (self.b_skill != 2) // use mouse emulation
88         {
89                 anglespeed = 1.5 * real_frametime;
90                 // 1.5 is the default cl_anglespeedkey & bot always has +speed
91                 self.v_angle_y = self.v_angle_y + anglespeed * CL_KeyState(KEY_LOOKLEFT) * 140;
92                 // 140 is default cl_yawspeed
93                 self.v_angle_y = self.v_angle_y - anglespeed * CL_KeyState(KEY_LOOKRIGHT) * 140;
94                 // 140 is default cl_yawspeed
95                 self.v_angle_x = self.v_angle_x - anglespeed * CL_KeyState(KEY_LOOKUP) * 150;
96                 // 150 is default cl_pitchspeed
97                 self.v_angle_x = self.v_angle_x + anglespeed * CL_KeyState(KEY_LOOKDOWN) * 150;
98                 // 150 is default cl_pitchspeed
99         }
100         else
101         {
102                 view_x = angcomp(self.b_angle_x, self.v_angle_x);
103                 view_y = angcomp(self.b_angle_y, self.v_angle_y);
104                 if (vlen(view) > 30)
105                 {
106                         self.mouse_emu = self.mouse_emu + (view * 30);
107                         if (vlen(self.mouse_emu) > 180)
108                                 self.mouse_emu = normalize(self.mouse_emu) * 180;
109                 }
110                 else
111                         self.mouse_emu = view * (1 / real_frametime);
112                 self.v_angle = self.v_angle + self.mouse_emu * real_frametime;
113
114
115         }
116         if (self.v_angle_x > 80)
117                 self.v_angle_x = 80;
118         else if (self.v_angle_x < -70)
119                 self.v_angle_x = -70;
120
121         if (self.v_angle_z > 50)
122                 self.v_angle_z = 50;
123         else if (self.v_angle_z < -50)
124                 self.v_angle_z = -50;
125         self.v_angle_y = frik_anglemod(self.v_angle_y);
126
127 };
128
129 /*
130 =========================================
131
132 Stuff mimicking sv_user.c
133
134 =========================================
135 */
136 void() SV_UserFriction =
137 {
138         local vector vel;
139         local float sped, friction, newspeed;
140
141       vel = self.velocity;
142         vel_z =0;
143         sped = vlen(vel);
144         vel = self.velocity;
145
146         if (!sped)
147                 return;
148
149     friction = sv_friction;
150      if (sped < sv_stopspeed)
151                 newspeed = sped - real_frametime * sv_stopspeed * friction;
152         else
153                 newspeed = sped - real_frametime * sped * friction;
154
155         if (newspeed < 0)
156                 newspeed = 0;
157         newspeed = newspeed / sped;
158
159         self.velocity_y = vel_y * newspeed;
160         self.velocity_x = vel_x * newspeed;
161 };
162 void() SV_WaterJump =
163 {
164         if (time > self.teleport_time || !self.waterlevel)
165         {
166                 self.flags = self.flags - (self.flags & FL_WATERJUMP);
167                 self.teleport_time = 0;
168         }
169         self.velocity_x = self.movedir_x;
170         self.velocity_y = self.movedir_y;
171 };
172
173 void() DropPunchAngle =
174 {
175         local float len;
176         len = vlen(self.punchangle);
177         self.punchangle = normalize(self.punchangle);
178         len = len - 10 * real_frametime;
179         if (len < 0)
180                 len = 0;
181         self.punchangle = self.punchangle * len;
182 };
183
184
185 void(vector wishvel) SV_AirAccelerate =
186 {
187         local float addspeed, wishspd, accelspeed, currentspeed;
188
189         wishspd = vlen(wishvel);
190         wishvel = normalize(wishvel);
191         if (wishspd > 30)
192                 wishspd = 30;
193         currentspeed = self.velocity * wishvel;
194         addspeed = wishspd - currentspeed;
195         if (addspeed <= 0)
196                 return;
197         accelspeed = 10 * sv_accelerate * wishspd * real_frametime;
198         if (accelspeed > addspeed)
199                 accelspeed = addspeed;
200
201         self.velocity = self.velocity + accelspeed * wishvel;
202 };
203
204 void(vector wishvel) SV_Accelerate =
205 {
206         local float addspeed, wishspd, accelspeed, currentspeed;
207
208         wishspd = vlen(wishvel);
209         wishvel = normalize(wishvel);
210
211         currentspeed = self.velocity * wishvel;
212         addspeed = wishspd - currentspeed;
213         if (addspeed <= 0)
214                 return;
215         accelspeed = sv_accelerate * wishspd * real_frametime;
216         if (accelspeed > addspeed)
217                 accelspeed = addspeed;
218
219         self.velocity = self.velocity + accelspeed * wishvel;
220 };
221 void() SV_WaterMove =
222 {
223         local vector wishvel;
224         local float wishspeed, addspeed, cspeed, newspeed;
225         makevectors(self.v_angle);
226         wishvel = v_right * self.movement_y + v_forward * self.movement_x;
227
228         if (self.movement == '0 0 0')
229                 wishvel_z = wishvel_z - 60;
230         else
231                 wishvel_z = wishvel_z + self.movement_z;
232         wishspeed = vlen(wishvel);
233
234         if (wishspeed > sv_maxspeed)
235         {
236                 wishvel = (sv_maxspeed / wishspeed) * wishvel;
237                 wishspeed = sv_maxspeed;
238         }
239         wishspeed = wishspeed * 0.7;
240         cspeed = vlen(self.velocity);
241         if (cspeed)
242         {
243                 newspeed = cspeed - (real_frametime * cspeed * sv_friction);
244                 if (newspeed < 0)
245                         newspeed = 0;
246                self.velocity = self.velocity * (newspeed / cspeed);
247
248         }
249         else
250                 newspeed = 0;
251
252         if (!wishspeed)
253                 return;
254         addspeed = wishspeed - newspeed;
255         if (addspeed <= 0)
256                 return;
257         wishvel = normalize(wishvel);
258         cspeed = sv_accelerate * wishspeed * real_frametime;
259         if (cspeed > addspeed)
260                 cspeed = addspeed;
261         self.velocity = self.velocity + cspeed * wishvel;
262 };
263 void() SV_AirMove =
264 {
265         local vector wishvel, vangle;
266
267         vangle = self.v_angle;
268         vangle_x = vangle_z = 0;
269         makevectors(vangle);
270         if (time < self.teleport_time && (self.movement_x < 0))
271                 self.movement_x = 0;
272         wishvel = v_right * self.movement_y + v_forward * self.movement_x;
273
274
275         if (self.movetype != MOVETYPE_WALK)
276                 wishvel_z = self.movement_z;
277         else
278                 wishvel_z = 0;
279         if (vlen(wishvel) > sv_maxspeed)
280                 wishvel = normalize(wishvel) * sv_maxspeed;
281         if (self.movetype == MOVETYPE_NOCLIP)
282                 self.velocity = wishvel;
283         else if (self.flags & FL_ONGROUND)
284         {
285                 SV_UserFriction();
286                 SV_Accelerate(wishvel);
287         }
288         else
289                 SV_AirAccelerate (wishvel);
290 };
291
292 void() SV_ClientThink =
293 {
294         local vector vangle;
295
296         if (self.movetype == MOVETYPE_NONE)
297                 return;
298         DropPunchAngle();
299         if (self.health <= 0)
300                 return;
301         self.v_angle_z = 0; // V_CalcRoll removed, sucks
302         self.angles_z = self.v_angle_z * 4;
303         vangle = self.v_angle + self.punchangle;
304         if (!self.fixangle)
305         {
306                 self.angles_x = (vangle_x / -3);
307                 self.angles_y = vangle_y;
308         } else
309         {
310                 self.v_angle = self.angles;
311                 self.fixangle = 0;
312         }
313         if (self.flags & FL_WATERJUMP)
314         {
315                 SV_WaterJump();
316                 return;
317         }
318         if ((self.waterlevel >= 2) && (self.movetype != MOVETYPE_NOCLIP))
319         {
320                 SV_WaterMove();
321                 return;
322         }
323         SV_AirMove();
324
325 };
326 /*
327 =========================================
328
329 Stuff mimicking sv_phys.c
330
331 =========================================
332 */
333
334 float() SV_RunThink  =
335 {
336         local float thinktime, bkuptime;
337         thinktime = self.nextthink;
338         bkuptime = time;
339         if (thinktime <= 0 || thinktime > (time + real_frametime))
340                 return TRUE;
341         if (thinktime < time)
342                 thinktime = time;
343         self.nextthink = 0;
344         time = thinktime;
345         other = world;
346         makevectors(self.v_angle); // hack
347         self.think();
348         time = bkuptime;
349         return TRUE;
350 };
351
352 void(float scal) SV_AddGravity =
353 {
354         self.velocity_z = self.velocity_z - (scal * sv_gravity * real_frametime);
355 };
356
357 float() SV_CheckWater =
358 {
359         local vector point;
360         local float cont;
361
362         point_x = self.origin_x;
363         point_y = self.origin_y;
364         self.waterlevel = 0;
365         self.watertype = CONTENT_EMPTY;
366         point_z = self.origin_z + self.mins_z + 1;
367         cont = pointcontents(point);
368         if (cont <= CONTENT_WATER)
369         {
370                 self.watertype = cont;
371                 self.waterlevel = 1;
372                 point_z = self.origin_z + (self.mins_z + self.maxs_z) * 0.5;
373                 cont = pointcontents(point);
374                 if (cont <= CONTENT_WATER)
375                 {
376                         self.waterlevel = 2;
377                         point_z = self.origin_z + self.view_ofs_z;
378                         cont = pointcontents(point);
379                         if (cont <= CONTENT_WATER)
380                                 self.waterlevel = 3;
381                 }
382         }
383         return (self.waterlevel > 1);
384
385 };
386 void() RemoveThud = // well sometimes
387 {
388         local entity oself;
389         if (other == world)
390         {
391                 if (self.flags & FL_ONGROUND)
392                 {
393                         self.flags = self.flags - FL_ONGROUND;
394                 }
395         }
396         else
397         {
398                 if (other.solid == SOLID_BSP && (self.flags & FL_ONGROUND))
399                 {
400                         // RM: Does this break anything?
401                         // If not, then some more thuds have been removed.
402                         self.flags = self.flags - FL_ONGROUND;
403                 }
404                 if (other == self.owner)
405                         return;
406                 if (self.owner.solid == SOLID_NOT)
407                         return;
408                 oself = other;
409                 other = self.owner;
410                 self = oself;
411                 if (self.solid == SOLID_BSP)
412                         if (self.touch)
413                                 self.touch();
414         }
415
416 };
417 void() SV_CheckOnGround =
418 {
419         local vector org, v;
420         org = self.origin;
421         local float currentflags;
422         currentflags = self.flags;
423         self.flags = self.flags | FL_ONGROUND | FL_PARTIALGROUND;
424         walkmove(0,0); // perform C touch function
425         self.flags = currentflags | FL_ONGROUND;
426         if ((org_x != self.origin_x) || (org_y != self.origin_y))
427                 org = self.origin;
428         else
429                 self.origin = org;
430         v = org;
431         v_z = self.maxs_z + org_z + 1;
432         traceline (org, v, TRUE, self);
433         if ((self.waterlevel == 3) && (self.movetype == MOVETYPE_WALK))
434                 self.flags = self.flags - FL_ONGROUND;
435         else if ((trace_plane_normal_z <= 0.7) && (trace_fraction != 1))
436                 self.flags = self.flags - FL_ONGROUND;
437         else if (!droptofloor(0,0))
438                 self.flags = self.flags - FL_ONGROUND;
439         else if (org_z - self.origin_z < 2)
440                 self.flags = self.flags | FL_ONGROUND;
441         else
442                 self.flags = self.flags - FL_ONGROUND;
443         setorigin(self, org);
444 };
445 // Thanks to Alan Kivlin for this function
446 // modified heavily by me
447 float(vector dir) botCheckForStep =
448 {
449         local vector currentorigin, v;
450         local float currentflags, yaw, stepdistance, movedistance;
451         currentorigin = self.origin;
452         currentflags = self.flags;
453         self.flags = FL_ONGROUND | FL_PARTIALGROUND;
454         dir = normalize(dir);
455         dir_z = 0;
456         yaw = vectoyaw(dir);
457         if(walkmove(yaw, 3))
458         {
459                 if(droptofloor(0,0))
460                 {
461                         stepdistance = self.origin_z - currentorigin_z;
462                         v = self.origin - currentorigin;
463                         v_z = 0;
464                         movedistance = vlen(v);
465                         if((stepdistance > 0 && stepdistance <= 16) && movedistance != 0)
466                         {
467                                 self.flags = currentflags | FL_PARTIALGROUND;
468                                 return 1;
469                         }
470                 }
471         }
472         self.flags = currentflags;
473         setorigin(self, currentorigin);
474         return 0;
475 };
476 // this is merely here to fix a problem with e3m5
477 void(vector dir) BruteForceStep =
478 {
479         local vector currentorigin;
480         local float currentflags, i, len;
481
482         currentorigin = self.origin;
483         currentflags = self.flags;
484         len = vlen(dir);
485         if (len > 16)
486                 dir = normalize(dir) * 16;
487
488         setorigin(self, currentorigin + dir);
489
490         while(i < 18 && !walkmove(0, 0))
491         {
492                 self.origin_z = currentorigin_z + i;
493                 i = i + 2;
494         }
495         self.flags = currentflags;
496         if (i >=18)
497                 setorigin(self, currentorigin);
498 };
499
500 void() PostPhysics =
501 {
502         local vector obstr, org;
503         local float back, dst,cflags;
504
505         self = self.owner;
506
507         self.velocity = self.velocity - self.phys_obj.dest1 + self.phys_obj.velocity;
508         if (self.phys_obj.dest2 == self.origin)
509         {
510                 setorigin(self, self.phys_obj.origin);
511                 // might've been moved during other person's physics
512                 // (teleporters / plats)
513
514                 if (self.movetype == MOVETYPE_WALK)
515                 {
516
517                         if (self.phys_obj.dest1_x || self.phys_obj.dest1_y)
518                         {
519                                 if ((self.flags & FL_ONGROUND) || (self.waterlevel <= 2))
520                                 {
521                                         obstr = self.phys_obj.movedir  - self.origin;
522                                         obstr_z = 0;
523                                         if (vlen(obstr) > 0.1)
524                                         {
525                                                 dst = vlen(obstr);
526                                                 back = vectoyaw(obstr);
527                                                 cflags = self.flags;
528                                                 self.flags = self.flags | FL_PARTIALGROUND;
529                                                 if(walkmove(back, dst))
530                                                 {
531                                                         self.flags = cflags;
532                                                         self.phys_obj.dest1_z = 0;
533                                                         self.velocity = self.velocity + self.phys_obj.dest1 - self.phys_obj.velocity;
534                                                 }
535                                                 else
536                                                 {
537                                                         if (dst > 1)
538                                                                 frik_obstructed(obstr, FALSE);
539
540                                                         org = self.origin;
541                                                         self.flags = cflags;
542                                                         obstr = self.phys_obj.dest1;
543                                                         obstr_x = 0;
544                                                         if (!botCheckForStep(obstr))
545                                                         {
546                                                                 obstr = self.phys_obj.dest1;
547                                                                 obstr_y = 0;
548                                                                 if (!botCheckForStep(obstr))
549                                                                 {
550                                                                         // if no steps were found, bot is really obstucted
551                                                                         BruteForceStep(self.phys_obj.dest1);
552                                                                 }
553                                                         }
554                                                 }
555                                         }
556                                 }
557                         }
558                 }
559         }
560
561         SV_CheckOnGround();
562
563         PlayerPostThink();
564         BotAI();
565         self.dmg_take = self.dmg_save = 0;
566
567 };
568 // Avoid calling BotAI and the physics at the same time
569 // Can trip the runaway loop counter
570
571 void() SV_FlyMove =
572 {
573         // This is nothing like the Quake function.
574
575         if (self.phys_obj == world)
576         {
577                 self.phys_obj = find(world,classname,"phys_obj");
578                 while (self.phys_obj.owner != self)
579                 {
580                         self.phys_obj = find(self.phys_obj,classname,"phys_obj");
581                         if (self.phys_obj == world)
582                         {
583                                 error("No physics entity spawned!\nMake sure BotInit was called\n");
584                         }
585                 }
586         }
587
588         setmodel (self.phys_obj, string_null);
589         self.phys_obj.movetype = MOVETYPE_STEP;
590
591         self.phys_obj.solid = SOLID_TRIGGER;
592         self.phys_obj.touch = RemoveThud;
593         setsize(self.phys_obj, self.mins, self.maxs);
594         self.phys_obj.dest2 = self.phys_obj.origin = self.origin;
595         self.phys_obj.watertype = 0;
596         self.phys_obj.movedir = self.origin + real_frametime * self.velocity;
597         self.phys_obj.dest1 = self.phys_obj.velocity = self.velocity;
598         self.phys_obj.velocity_z = self.phys_obj.velocity_z + sv_gravity * real_frametime;
599         self.phys_obj.flags = 0;
600         self.phys_obj.think = PostPhysics;
601         self.phys_obj.nextthink = time;
602 };
603
604
605 void() SV_Physics_Toss =
606 {
607         if (!SV_RunThink())
608                 return;
609         if (self.flags & FL_ONGROUND)
610         {
611                 self.velocity = '0 0 0';
612                 BotAI();
613                 return;
614         }
615         if (self.movetype != MOVETYPE_FLY)
616                 SV_AddGravity(1);
617         self.angles = self.angles + real_frametime * self.avelocity;
618         SV_FlyMove();
619
620 };
621 void() SV_Physics_Client =
622 {
623
624         PlayerPreThink();
625
626         if (self.movetype == MOVETYPE_NONE)
627         {
628                 if (!SV_RunThink())
629                         return;
630                 PlayerPostThink();
631                 BotAI();
632
633         }
634         else if ((self.movetype == MOVETYPE_WALK) || (self.movetype == MOVETYPE_STEP))
635         {
636                 if (!SV_RunThink())
637                         return;
638             if (!(SV_CheckWater()) && (!(self.flags & FL_WATERJUMP)))
639                         SV_AddGravity(1);
640                 SV_FlyMove();
641         }
642         else if ((self.movetype == MOVETYPE_TOSS) || (self.movetype == MOVETYPE_BOUNCE))
643         {
644                 SV_Physics_Toss();
645         }
646         else if (self.movetype == MOVETYPE_FLY)
647         {
648                 if (!SV_RunThink())
649                         return;
650                 SV_FlyMove();
651         }
652         else if (self.movetype == MOVETYPE_NOCLIP)
653         {
654                 if (!SV_RunThink())
655                         return;
656                 self.origin = self.origin + real_frametime * self.velocity;
657
658                 PlayerPostThink();
659                 BotAI();
660         }
661         else
662                 error ("SV_Physics_Client: Bad Movetype (BOT)");
663
664 };
665
666