]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/bot/bot_phys.qc
commit 1.2.1 game media
[divverent/nexuiz.git] / data / qcsrc / bot / bot_phys.qc
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, start, stop;
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 // if the leading edge is over a dropoff, increase friction
150
151         start_x = stop_x = self.origin_x + vel_x / (sped * 16);
152         start_y = stop_y = self.origin_y + vel_y / (sped * 16);
153         start_z = self.origin_z + self.mins_z;
154         stop_z = start_z - 34;
155
156         traceline(start, stop, TRUE, self);
157
158         if (trace_fraction == 1)
159                 friction = sv_friction * 2; // 2 is default edgefriction, removed for QW compatability
160         else
161                 friction = sv_friction;
162      if (sped < sv_stopspeed)
163                 newspeed = sped - real_frametime * sv_stopspeed * friction;
164         else
165                 newspeed = sped - real_frametime * sped * friction;
166
167         if (newspeed < 0)
168                 newspeed = 0;
169         newspeed = newspeed / sped;
170
171         self.velocity_y = vel_y * newspeed;
172         self.velocity_x = vel_x * newspeed;
173 };
174 void() SV_WaterJump =
175 {
176         if (time > self.teleport_time || !self.waterlevel)
177         {
178                 self.flags = self.flags - (self.flags & FL_WATERJUMP);
179                 self.teleport_time = 0;
180         }
181         self.velocity_x = self.movedir_x;
182         self.velocity_y = self.movedir_y;
183 };
184
185 void() DropPunchAngle =
186 {
187         local float len;
188         len = vlen(self.punchangle);
189         self.punchangle = normalize(self.punchangle);
190         len = len - 10 * real_frametime;
191         if (len < 0)
192                 len = 0;
193         self.punchangle = self.punchangle * len;
194 };
195
196
197 void(vector wishvel) SV_AirAccelerate =
198 {
199         local float addspeed, wishspd, accelspeed, currentspeed;
200
201         wishspd = vlen(wishvel);
202         wishvel = normalize(wishvel);
203         if (wishspd > 30)
204                 wishspd = 30;
205         currentspeed = self.velocity * wishvel;
206         addspeed = wishspd - currentspeed;
207         if (addspeed <= 0)
208                 return;
209         accelspeed = 10 * sv_accelerate * wishspd * real_frametime;
210         if (accelspeed > addspeed)
211                 accelspeed = addspeed;
212         
213         self.velocity = self.velocity + accelspeed * wishvel;
214 };
215
216 void(vector wishvel) SV_Accelerate =
217 {
218         local float addspeed, wishspd, accelspeed, currentspeed;
219
220         wishspd = vlen(wishvel);
221         wishvel = normalize(wishvel);
222
223         currentspeed = self.velocity * wishvel;
224         addspeed = wishspd - currentspeed;
225         if (addspeed <= 0)
226                 return;
227         accelspeed = sv_accelerate * wishspd * real_frametime;
228         if (accelspeed > addspeed)
229                 accelspeed = addspeed;
230         
231         self.velocity = self.velocity + accelspeed * wishvel;   
232 };
233 void() SV_WaterMove =
234 {
235         local vector wishvel;
236         local float wishspeed, addspeed, cspeed, newspeed;
237         makevectors(self.v_angle);
238         wishvel = v_right * self.movement_y + v_forward * self.movement_x;
239         
240         if (self.movement == '0 0 0')
241                 wishvel_z = wishvel_z - 60;
242         else
243                 wishvel_z = wishvel_z + self.movement_z;
244         wishspeed = vlen(wishvel);
245
246         if (wishspeed > sv_maxspeed)
247         {
248                 wishvel = (sv_maxspeed / wishspeed) * wishvel;
249                 wishspeed = sv_maxspeed;
250         }
251         wishspeed = wishspeed * 0.7;
252         cspeed = vlen(self.velocity);
253         if (cspeed)
254         {
255                 newspeed = cspeed - (real_frametime * cspeed * sv_friction);
256                 if (newspeed < 0)
257                         newspeed = 0;
258                self.velocity = self.velocity * (newspeed / cspeed);
259
260         }
261         else
262                 newspeed = 0;
263
264         if (!wishspeed)
265                 return;
266         addspeed = wishspeed - newspeed;
267         if (addspeed <= 0)
268                 return;
269         wishvel = normalize(wishvel);
270         cspeed = sv_accelerate * wishspeed * real_frametime;
271         if (cspeed > addspeed)
272                 cspeed = addspeed;
273         self.velocity = self.velocity + cspeed * wishvel;
274 };
275 void() SV_AirMove =
276 {
277         local vector wishvel, vangle;
278
279         vangle = self.v_angle;
280         vangle_x = vangle_z = 0;
281         makevectors(vangle);
282         if (time < self.teleport_time && (self.movement_x < 0))
283                 self.movement_x = 0;
284         wishvel = v_right * self.movement_y + v_forward * self.movement_x;
285
286
287         if (self.movetype != MOVETYPE_WALK)
288                 wishvel_z = self.movement_z;
289         else
290                 wishvel_z = 0;
291         if (vlen(wishvel) > sv_maxspeed)
292                 wishvel = normalize(wishvel) * sv_maxspeed;
293         if (self.movetype == MOVETYPE_NOCLIP)
294                 self.velocity = wishvel;
295         else if (self.flags & FL_ONGROUND)
296         {
297                 SV_UserFriction();
298                 SV_Accelerate(wishvel);
299         }
300         else
301                 SV_AirAccelerate (wishvel);
302 };
303
304 void() SV_ClientThink = 
305 {
306         local vector vangle;
307
308         if (self.movetype == MOVETYPE_NONE)
309                 return;
310         DropPunchAngle(); 
311         if (self.health <= 0)
312                 return;
313         self.v_angle_z = 0; // V_CalcRoll removed, sucks
314         self.angles_z = self.v_angle_z * 4;
315         vangle = self.v_angle + self.punchangle;
316         if (!self.fixangle)
317         {
318                 self.angles_x = (vangle_x / -3);
319                 self.angles_y = vangle_y;
320         } else
321         {
322                 self.v_angle = self.angles;
323                 self.fixangle = 0;
324         }
325         if (self.flags & FL_WATERJUMP)
326         {
327                 SV_WaterJump();
328                 return;
329         }
330         if ((self.waterlevel >= 2) && (self.movetype != MOVETYPE_NOCLIP))
331         {
332                 SV_WaterMove();
333                 return;
334         }
335         SV_AirMove();
336
337 };
338 /*
339 =========================================
340
341 Stuff mimicking sv_phys.c
342
343 =========================================
344 */
345
346 float() SV_RunThink  =
347 {
348         local float thinktime, bkuptime;
349         thinktime = self.nextthink;
350         bkuptime = time;
351         if (thinktime <= 0 || thinktime > (time + real_frametime))
352                 return TRUE;
353         if (thinktime < time)
354                 thinktime = time;
355         self.nextthink = 0;
356         time = thinktime; 
357         other = world;
358         makevectors(self.v_angle); // hack
359         self.think();
360         time = bkuptime;
361         return TRUE;
362 };
363
364 void(float scale) SV_AddGravity =
365 {
366         self.velocity_z = self.velocity_z - (scale * sv_gravity * real_frametime);
367 };
368
369 float() SV_CheckWater =
370 {
371         local vector point;
372         local float cont;
373
374         point_x = self.origin_x;
375         point_y = self.origin_y;
376         self.waterlevel = 0;
377         self.watertype = CONTENT_EMPTY;
378         point_z = self.origin_z + self.mins_z + 1;
379         cont = pointcontents(point);
380         if (cont <= CONTENT_WATER)
381         {
382                 self.watertype = cont;
383                 self.waterlevel = 1;
384                 point_z = self.origin_z + (self.mins_z + self.maxs_z) * 0.5;
385                 cont = pointcontents(point);
386                 if (cont <= CONTENT_WATER)
387                 {
388                         self.waterlevel = 2;
389                         point_z = self.origin_z + self.view_ofs_z;
390                         cont = pointcontents(point);
391                         if (cont <= CONTENT_WATER)
392                                 self.waterlevel = 3;
393                 }
394         }
395         return (self.waterlevel > 1);
396
397 };
398 void() RemoveThud = // well sometimes
399
400         local entity oself;
401         if (other == world)
402         {
403                 if (self.flags & FL_ONGROUND)
404                 {
405                         self.flags = self.flags - FL_ONGROUND; 
406                 }
407         }
408         else
409         {
410                 if (other.solid == SOLID_BSP && (self.flags & FL_ONGROUND))     
411                 {
412                         // RM: Does this break anything?
413                         // If not, then some more thuds have been removed.
414                         self.flags = self.flags - FL_ONGROUND;
415                 }               
416                 if (other == self.owner)
417                         return;
418                 if (self.owner.solid == SOLID_NOT)
419                         return;
420                 oself = other;
421                 other = self.owner;
422                 self = oself;
423                 if (self.solid == SOLID_BSP)
424                         if (self.touch)
425                                 self.touch();
426         }
427
428 };
429 void() SV_CheckOnGround =
430 {
431         local vector org, v;
432         org = self.origin;
433         local float currentflags;
434         currentflags = self.flags;
435         self.flags = self.flags | FL_ONGROUND | FL_PARTIALGROUND;
436         walkmove(0,0); // perform C touch function
437         self.flags = currentflags | FL_ONGROUND;
438         if ((org_x != self.origin_x) || (org_y != self.origin_y))
439                 org = self.origin;
440         else
441                 self.origin = org;
442         v = org;
443         v_z = self.maxs_z + org_z + 1;
444         traceline (org, v, TRUE, self); 
445         if ((self.waterlevel == 3) && (self.movetype == MOVETYPE_WALK))
446                 self.flags = self.flags - FL_ONGROUND;
447         else if ((trace_plane_normal_z <= 0.7) && (trace_fraction != 1))
448                 self.flags = self.flags - FL_ONGROUND;
449         else if (!droptofloor(0,0))
450                 self.flags = self.flags - FL_ONGROUND;
451         else if (org_z - self.origin_z < 2)
452                 self.flags = self.flags | FL_ONGROUND;
453         else
454                 self.flags = self.flags - FL_ONGROUND;
455         setorigin(self, org);
456 };
457 // Thanks to Alan Kivlin for this function
458 // modified heavily by me
459 float(vector dir) botCheckForStep =
460 {
461         local vector currentorigin, v;
462         local float currentflags, yaw, stepdistance, movedistance; 
463         currentorigin = self.origin;
464         currentflags = self.flags; 
465         self.flags = FL_ONGROUND | FL_PARTIALGROUND; 
466         dir = normalize(dir);
467         dir_z = 0;
468         yaw = vectoyaw(dir); 
469         if(walkmove(yaw, 3))
470         {       
471                 if(droptofloor(0,0))
472                 {       
473                         stepdistance = self.origin_z - currentorigin_z;
474                         v = self.origin - currentorigin;
475                         v_z = 0;
476                         movedistance = vlen(v); 
477                         if((stepdistance > 0 && stepdistance <= 16) && movedistance != 0)
478                         {
479                                 self.flags = currentflags | FL_PARTIALGROUND;
480                                 return 1;
481                         }
482                 }
483         }       
484         self.flags = currentflags;
485         setorigin(self, currentorigin);
486         return 0;
487 }; 
488 // this is merely here to fix a problem with e3m5
489 void(vector dir) BruteForceStep =
490 {
491         local vector currentorigin;
492         local float currentflags, i, len;
493
494         currentorigin = self.origin;
495         currentflags = self.flags;
496         len = vlen(dir);
497         if (len > 16)
498                 dir = normalize(dir) * 16;
499
500         setorigin(self, currentorigin + dir);
501
502         while(i < 18 && !walkmove(0, 0))
503         {
504                 self.origin_z = currentorigin_z + i;
505                 i = i + 2;
506         }
507         self.flags = currentflags;
508         if (i >=18)
509                 setorigin(self, currentorigin);
510 };
511
512 void() PostPhysics =
513 {
514         local vector obstr, org;
515         local float back, dst,cflags;
516
517         self = self.owner;
518
519         self.velocity = self.velocity - self.phys_obj.dest1 + self.phys_obj.velocity;
520         if (self.phys_obj.dest2 == self.origin)
521         {
522                 setorigin(self, self.phys_obj.origin); 
523                 // might've been moved during other person's physics
524                 // (teleporters / plats)
525
526                 if (self.movetype == MOVETYPE_WALK)
527                 {
528
529                         if (self.phys_obj.dest1_x || self.phys_obj.dest1_y)
530                         {
531                                 if ((self.flags & FL_ONGROUND) || (self.waterlevel <= 2))
532                                 {
533                                         obstr = self.phys_obj.movedir  - self.origin;
534                                         obstr_z = 0;
535                                         if (vlen(obstr) > 0.1)
536                                         {
537                                                 dst = vlen(obstr);
538                                                 back = vectoyaw(obstr);
539                                                 cflags = self.flags;
540                                                 self.flags = self.flags | FL_PARTIALGROUND;
541                                                 if(walkmove(back, dst))
542                                                 {
543                                                         self.flags = cflags;
544                                                         self.phys_obj.dest1_z = 0;
545                                                         self.velocity = self.velocity + self.phys_obj.dest1 - self.phys_obj.velocity;
546                                                 }
547                                                 else
548                                                 {
549                                                         if (dst > 1)
550                                                                 frik_obstructed(obstr, FALSE); 
551
552                                                         org = self.origin;
553                                                         self.flags = cflags;
554                                                         obstr = self.phys_obj.dest1;
555                                                         obstr_x = 0;
556                                                         if (!botCheckForStep(obstr))
557                                                         {
558                                                                 obstr = self.phys_obj.dest1;
559                                                                 obstr_y = 0;
560                                                                 if (!botCheckForStep(obstr))
561                                                                 {
562                                                                         // if no steps were found, bot is really obstucted
563                                                                         BruteForceStep(self.phys_obj.dest1);
564                                                                 }
565                                                         }       
566                                                 }
567                                         }
568                                 }
569                         }
570                 }
571         }
572
573         SV_CheckOnGround();
574
575         PlayerPostThink();
576         BotAI();
577         self.dmg_take = self.dmg_save = 0;
578
579 };
580 // Avoid calling BotAI and the physics at the same time
581 // Can trip the runaway loop counter
582
583 void() SV_FlyMove =
584 {
585         // This is nothing like the Quake function.
586
587         if (self.phys_obj == world)
588         {
589                 self.phys_obj = find(world,classname,"phys_obj");
590                 while (self.phys_obj.owner != self)
591                 {
592                         self.phys_obj = find(self.phys_obj,classname,"phys_obj");
593                         if (self.phys_obj == world)
594                         {
595                                 error("No physics entity spawned!\nMake sure BotInit was called\n");
596                         }
597                 }
598         }
599
600         setmodel (self.phys_obj, string_null);
601         self.phys_obj.movetype = MOVETYPE_STEP;
602         
603         self.phys_obj.solid = SOLID_TRIGGER;
604         self.phys_obj.touch = RemoveThud;
605         setsize(self.phys_obj, self.mins, self.maxs);
606         self.phys_obj.dest2 = self.phys_obj.origin = self.origin;
607         self.phys_obj.watertype = 0;
608         self.phys_obj.movedir = self.origin + real_frametime * self.velocity;
609         self.phys_obj.dest1 = self.phys_obj.velocity = self.velocity;
610         self.phys_obj.velocity_z = self.phys_obj.velocity_z + sv_gravity * real_frametime;
611         self.phys_obj.flags = 0;
612         self.phys_obj.think = PostPhysics;
613         self.phys_obj.nextthink = time;
614 };
615
616
617 void() SV_Physics_Toss =
618 {
619         if (!SV_RunThink())
620                 return;
621         if (self.flags & FL_ONGROUND)
622         {
623                 self.velocity = '0 0 0';
624                 BotAI();
625                 return;
626         }
627         if (self.movetype != MOVETYPE_FLY)
628                 SV_AddGravity(1);
629         self.angles = self.angles + real_frametime * self.avelocity;
630         SV_FlyMove();
631
632 };
633 void() SV_Physics_Client =
634 {
635
636         PlayerPreThink();
637
638         if (self.movetype == MOVETYPE_NONE)
639         {
640                 if (!SV_RunThink())
641                         return;
642                 PlayerPostThink();
643                 BotAI();
644
645         }
646         else if ((self.movetype == MOVETYPE_WALK) || (self.movetype == MOVETYPE_STEP)) 
647         {
648                 if (!SV_RunThink())
649                         return;
650             if (!(SV_CheckWater()) && (!(self.flags & FL_WATERJUMP)))
651                         SV_AddGravity(1);
652                 SV_FlyMove();
653         }
654         else if ((self.movetype == MOVETYPE_TOSS) || (self.movetype == MOVETYPE_BOUNCE))
655         {
656                 SV_Physics_Toss();
657         }
658         else if (self.movetype == MOVETYPE_FLY)
659         {
660                 if (!SV_RunThink())
661                         return;
662                 SV_FlyMove();
663         }
664         else if (self.movetype == MOVETYPE_NOCLIP)
665         {
666                 if (!SV_RunThink())
667                         return;
668                 self.origin = self.origin + real_frametime * self.velocity;
669
670                 PlayerPostThink();
671                 BotAI();
672         }
673         else
674                 error ("SV_Physics_Client: Bad Movetype (BOT)");
675
676 };
677
678