Increase the maximum number of mutators
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / bot / lib / steerlib / steerlib.qc
1 /**
2     Uniform pull towards a point
3 **/
4 vector steerlib_pull(vector point)
5 {SELFPARAM();
6     return normalize(point - self.origin);
7 }
8
9 /**
10     Uniform push from a point
11 **/
12 #define steerlib_push(point) normalize(self.origin - point)
13 /*
14 vector steerlib_push(vector point)
15 {
16     return normalize(self.origin - point);
17 }
18 */
19 /**
20     Pull toward a point, The further away, the stronger the pull.
21 **/
22 vector steerlib_arrive(vector point,float maximal_distance)
23 {SELFPARAM();
24     float distance;
25     vector direction;
26
27     distance = bound(0.001,vlen(self.origin - point),maximal_distance);
28     direction = normalize(point - self.origin);
29     return  direction * (distance / maximal_distance);
30 }
31
32 /**
33     Pull toward a point increasing the pull the closer we get
34 **/
35 vector steerlib_attract(vector point, float maximal_distance)
36 {SELFPARAM();
37     float distance;
38     vector direction;
39
40     distance = bound(0.001,vlen(self.origin - point),maximal_distance);
41     direction = normalize(point - self.origin);
42
43     return  direction * (1-(distance / maximal_distance));
44 }
45
46 vector steerlib_attract2(vector point, float min_influense,float max_distance,float max_influense)
47 {SELFPARAM();
48     float distance;
49     vector direction;
50     float influense;
51
52     distance  = bound(0.00001,vlen(self.origin - point),max_distance);
53     direction = normalize(point - self.origin);
54
55     influense = 1 - (distance / max_distance);
56     influense = min_influense + (influense * (max_influense - min_influense));
57
58     return  direction * influense;
59 }
60
61 /*
62 vector steerlib_attract2(vector point, float maximal_distance,float min_influense,float max_influense,float distance)
63 {
64     //float distance;
65     vector current_direction;
66     vector target_direction;
67     float i_target,i_current;
68
69     if(!distance)
70         distance = vlen(self.origin - point);
71
72     distance = bound(0.001,distance,maximal_distance);
73
74     target_direction = normalize(point - self.origin);
75     current_direction = normalize(self.velocity);
76
77     i_target = bound(min_influense,(1-(distance / maximal_distance)),max_influense);
78     i_current = 1 - i_target;
79
80     // i_target = bound(min_influense,(1-(distance / maximal_distance)),max_influense);
81
82     string s;
83     s = ftos(i_target);
84     bprint("IT: ",s,"\n");
85     s = ftos(i_current);
86     bprint("IC  : ",s,"\n");
87
88     return  normalize((target_direction * i_target) + (current_direction * i_current));
89 }
90 */
91 /**
92     Move away from a point.
93 **/
94 vector steerlib_repell(vector point,float maximal_distance)
95 {SELFPARAM();
96     float distance;
97     vector direction;
98
99     distance = bound(0.001,vlen(self.origin - point),maximal_distance);
100     direction = normalize(self.origin - point);
101
102     return  direction * (1-(distance / maximal_distance));
103 }
104
105 /**
106     Try to keep at ideal_distance away from point
107 **/
108 vector steerlib_standoff(vector point,float ideal_distance)
109 {SELFPARAM();
110     float distance;
111     vector direction;
112
113     distance = vlen(self.origin - point);
114
115
116     if(distance < ideal_distance)
117     {
118         direction = normalize(self.origin - point);
119         return direction * (distance / ideal_distance);
120     }
121
122     direction = normalize(point - self.origin);
123     return direction * (ideal_distance / distance);
124
125 }
126
127 /**
128     A random heading in a forward halfcicrle
129
130     use like:
131     self.target = steerlib_wander(256,32,self.target)
132
133     where range is the cicrle radius and tresh is how close we need to be to pick a new heading.
134 **/
135 vector steerlib_wander(float range,float tresh,vector oldpoint)
136 {SELFPARAM();
137     vector wander_point;
138     wander_point = v_forward - oldpoint;
139
140     if (vlen(wander_point) > tresh)
141         return oldpoint;
142
143     range = bound(0,range,1);
144
145     wander_point = self.origin + v_forward * 128;
146     wander_point = wander_point + randomvec() * (range * 128) - randomvec() * (range * 128);
147
148     return normalize(wander_point - self.origin);
149 }
150
151 /**
152     Dodge a point. dont work to well.
153 **/
154 vector steerlib_dodge(vector point,vector dodge_dir,float min_distance)
155 {SELFPARAM();
156     float distance;
157
158     distance = max(vlen(self.origin - point),min_distance);
159     if (min_distance < distance)
160         return '0 0 0';
161
162     return dodge_dir * (min_distance/distance);
163 }
164
165 /**
166     flocking by .flock_id
167     Group will move towards the unified direction while keeping close to eachother.
168 **/
169 .float flock_id;
170 vector steerlib_flock(float _radius, float standoff,float separation_force,float flock_force)
171 {SELFPARAM();
172     entity flock_member;
173     vector push = '0 0 0', pull = '0 0 0';
174     float ccount = 0;
175
176     flock_member = findradius(self.origin, _radius);
177     while(flock_member)
178     {
179         if(flock_member != self)
180         if(flock_member.flock_id == self.flock_id)
181         {
182             ++ccount;
183             push = push + (steerlib_repell(flock_member.origin,standoff) * separation_force);
184             pull = pull + (steerlib_arrive(flock_member.origin + flock_member.velocity, _radius) * flock_force);
185         }
186         flock_member = flock_member.chain;
187     }
188     return push + (pull* (1 / ccount));
189 }
190
191 /**
192     flocking by .flock_id
193     Group will move towards the unified direction while keeping close to eachother.
194     xy only version (for ground movers).
195 **/
196 vector steerlib_flock2d(float _radius, float standoff,float separation_force,float flock_force)
197 {SELFPARAM();
198     entity flock_member;
199     vector push = '0 0 0', pull = '0 0 0';
200     float ccount = 0;
201
202     flock_member = findradius(self.origin,_radius);
203     while(flock_member)
204     {
205         if(flock_member != self)
206         if(flock_member.flock_id == self.flock_id)
207         {
208             ++ccount;
209             push = push + (steerlib_repell(flock_member.origin, standoff) * separation_force);
210             pull = pull + (steerlib_arrive(flock_member.origin + flock_member.velocity, _radius) * flock_force);
211         }
212         flock_member = flock_member.chain;
213     }
214
215     push.z = 0;
216     pull.z = 0;
217
218     return push + (pull * (1 / ccount));
219 }
220
221 /**
222     All members want to be in the center, and keep away from eachother.
223     The furtehr form the center the more they want to be there.
224
225     This results in a aligned movement (?!) much like flocking.
226 **/
227 vector steerlib_swarm(float _radius, float standoff,float separation_force,float swarm_force)
228 {SELFPARAM();
229     entity swarm_member;
230     vector force = '0 0 0', center = '0 0 0';
231     float ccount = 0;
232
233     swarm_member = findradius(self.origin,_radius);
234
235     while(swarm_member)
236     {
237         if(swarm_member.flock_id == self.flock_id)
238         {
239             ++ccount;
240             center = center + swarm_member.origin;
241             force = force + (steerlib_repell(swarm_member.origin,standoff) * separation_force);
242         }
243         swarm_member = swarm_member.chain;
244     }
245
246     center = center * (1 / ccount);
247     force = force + (steerlib_arrive(center,_radius) * swarm_force);
248
249     return force;
250 }
251
252 /**
253     Steer towards the direction least obstructed.
254     Run four tracelines in a forward funnel, bias each diretion negative if something is found there.
255     You need to call makevectors() (or equivalent) before this function to set v_forward and v_right
256 **/
257 vector steerlib_traceavoid(float pitch,float length)
258 {SELFPARAM();
259     vector vup_left,vup_right,vdown_left,vdown_right;
260     float fup_left,fup_right,fdown_left,fdown_right;
261     vector upwish,downwish,leftwish,rightwish;
262     vector v_left,v_down;
263
264
265     v_left = v_right * -1;
266     v_down = v_up * -1;
267
268     vup_left = (v_forward + (v_left * pitch + v_up * pitch)) * length;
269     traceline(self.origin, self.origin +  vup_left,MOVE_NOMONSTERS,self);
270     fup_left = trace_fraction;
271
272     //te_lightning1(world,self.origin, trace_endpos);
273
274     vup_right = (v_forward + (v_right * pitch + v_up * pitch)) * length;
275     traceline(self.origin,self.origin + vup_right ,MOVE_NOMONSTERS,self);
276     fup_right = trace_fraction;
277
278     //te_lightning1(world,self.origin, trace_endpos);
279
280     vdown_left = (v_forward + (v_left * pitch + v_down * pitch)) * length;
281     traceline(self.origin,self.origin + vdown_left,MOVE_NOMONSTERS,self);
282     fdown_left = trace_fraction;
283
284     //te_lightning1(world,self.origin, trace_endpos);
285
286     vdown_right = (v_forward + (v_right * pitch + v_down * pitch)) * length;
287     traceline(self.origin,self.origin + vdown_right,MOVE_NOMONSTERS,self);
288     fdown_right = trace_fraction;
289
290     //te_lightning1(world,self.origin, trace_endpos);
291     upwish    = v_up    * (fup_left   + fup_right);
292     downwish  = v_down  * (fdown_left + fdown_right);
293     leftwish  = v_left  * (fup_left   + fdown_left);
294     rightwish = v_right * (fup_right  + fdown_right);
295
296     return (upwish+leftwish+downwish+rightwish) * 0.25;
297
298 }
299
300 /**
301     Steer towards the direction least obstructed.
302     Run tracelines in a forward trident, bias each direction negative if something is found there.
303 **/
304 vector steerlib_traceavoid_flat(float pitch, float length, vector vofs)
305 {SELFPARAM();
306     vector vt_left, vt_right,vt_front;
307     float f_left, f_right,f_front;
308     vector leftwish, rightwish,frontwish, v_left;
309
310     v_left = v_right * -1;
311
312
313     vt_front = v_forward * length;
314     traceline(self.origin + vofs, self.origin + vofs + vt_front,MOVE_NOMONSTERS,self);
315     f_front = trace_fraction;
316
317     vt_left = (v_forward + (v_left * pitch)) * length;
318     traceline(self.origin + vofs, self.origin + vofs + vt_left,MOVE_NOMONSTERS,self);
319     f_left = trace_fraction;
320
321     //te_lightning1(world,self.origin, trace_endpos);
322
323     vt_right = (v_forward + (v_right * pitch)) * length;
324     traceline(self.origin + vofs, self.origin + vofs + vt_right ,MOVE_NOMONSTERS,self);
325     f_right = trace_fraction;
326
327     //te_lightning1(world,self.origin, trace_endpos);
328
329     leftwish  = v_left    * f_left;
330     rightwish = v_right   * f_right;
331     frontwish = v_forward * f_front;
332
333     return normalize(leftwish + rightwish + frontwish);
334 }
335
336 float beamsweep_badpoint(vector point,float waterok)
337 {
338     float pc,pc2;
339
340     if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
341         return 1;
342
343     pc  = pointcontents(point);
344     pc2 = pointcontents(point - '0 0 1');
345
346     switch(pc)
347     {
348         case CONTENT_SOLID: break;
349         case CONTENT_SLIME: break;
350         case CONTENT_LAVA:  break;
351
352         case CONTENT_SKY:
353             return 1;
354
355         case CONTENT_EMPTY:
356             if (pc2 == CONTENT_SOLID)
357                 return 0;
358
359             if (pc2 == CONTENT_WATER)
360                 if(waterok)
361                     return 0;
362
363             break;
364
365         case CONTENT_WATER:
366             if(waterok)
367                 return 0;
368
369             break;
370     }
371
372     return 1;
373 }
374
375 //#define BEAMSTEER_VISUAL
376 float beamsweep(vector from, vector dir,float length, float step,float step_up, float step_down)
377 {SELFPARAM();
378     float i;
379     vector a,b,u,d;
380
381     u = '0 0 1' * step_up;
382     d = '0 0 1' * step_down;
383
384     traceline(from + u, from - d,MOVE_NORMAL,self);
385     if(trace_fraction == 1.0)
386         return 0;
387
388     if(beamsweep_badpoint(trace_endpos,0))
389         return 0;
390
391     a = trace_endpos;
392     for(i = 0; i < length; i += step)
393     {
394
395         b = a + dir * step;
396         tracebox(a + u,'-4 -4 -4','4 4 4', b + u,MOVE_NORMAL,self);
397         if(trace_fraction != 1.0)
398             return i / length;
399
400         traceline(b + u, b - d,MOVE_NORMAL,self);
401         if(trace_fraction == 1.0)
402             return i / length;
403
404         if(beamsweep_badpoint(trace_endpos,0))
405             return i / length;
406 #ifdef BEAMSTEER_VISUAL
407         te_lightning1(world,a+u,b+u);
408         te_lightning1(world,b+u,b-d);
409 #endif
410         a = trace_endpos;
411     }
412
413     return 1;
414 }
415
416 vector steerlib_beamsteer(vector dir, float length, float step, float step_up, float step_down)
417 {SELFPARAM();
418     float bm_forward, bm_right, bm_left,p;
419     vector vr,vl;
420
421     dir.z *= 0.15;
422     vr = vectoangles(dir);
423     //vr_x *= -1;
424
425     tracebox(self.origin + '0 0 1' * step_up, self.mins, self.maxs, ('0 0 1' * step_up) + self.origin +  (dir * length), MOVE_NOMONSTERS, self);
426     if(trace_fraction == 1.0)
427     {
428         //te_lightning1(self,self.origin,self.origin +  (dir * length));
429         return dir;
430     }
431
432
433
434
435     makevectors(vr);
436     bm_forward = beamsweep(self.origin, v_forward, length, step, step_up, step_down);
437
438     vr = normalize(v_forward + v_right * 0.125);
439     vl = normalize(v_forward - v_right * 0.125);
440
441     bm_right = beamsweep(self.origin, vr, length, step, step_up, step_down);
442     bm_left  = beamsweep(self.origin, vl, length, step, step_up, step_down);
443
444
445     p = bm_left + bm_right;
446     if(p == 2)
447     {
448         //te_lightning1(self,self.origin + '0 0 32',self.origin + '0 0 32' + vr * length);
449         //te_lightning1(self.tur_head,self.origin + '0 0 32',self.origin + '0 0 32' + vl * length);
450
451         return v_forward;
452     }
453
454     p = 2 - p;
455
456     vr = normalize(v_forward + v_right * p);
457     vl = normalize(v_forward - v_right * p);
458     bm_right = beamsweep(self.origin, vr, length, step, step_up, step_down);
459     bm_left  = beamsweep(self.origin, vl, length, step, step_up, step_down);
460
461
462     if(bm_left + bm_right < 0.15)
463     {
464         vr = normalize((v_forward*-1) + v_right * 0.75);
465         vl = normalize((v_forward*-1) - v_right * 0.75);
466
467         bm_right = beamsweep(self.origin, vr, length, step, step_up, step_down);
468         bm_left  = beamsweep(self.origin, vl, length, step, step_up, step_down);
469     }
470
471     //te_lightning1(self,self.origin + '0 0 32',self.origin + '0 0 32' + vr * length);
472     //te_lightning1(self.tur_head,self.origin + '0 0 32',self.origin + '0 0 32' + vl * length);
473
474     bm_forward *= bm_forward;
475     bm_right   *= bm_right;
476     bm_left    *= bm_left;
477
478     vr = vr * bm_right;
479     vl = vl * bm_left;
480
481     return normalize(vr + vl);
482
483 }
484
485
486 //////////////////////////////////////////////
487 //     Testting                             //
488 // Everything below this point is a mess :D //
489 //////////////////////////////////////////////
490 //#define TLIBS_TETSLIBS
491 #ifdef TLIBS_TETSLIBS
492 void flocker_die()
493 {SELFPARAM();
494         Send_Effect(EFFECT_ROCKET_EXPLODE, self.origin, '0 0 0', 1);
495
496     self.owner.cnt += 1;
497     self.owner = world;
498
499     self.nextthink = time;
500     self.think = SUB_Remove;
501 }
502
503
504 void flocker_think()
505 {SELFPARAM();
506     vector dodgemove,swarmmove;
507     vector reprellmove,wandermove,newmove;
508
509     self.angles_x = self.angles.x * -1;
510     makevectors(self.angles);
511     self.angles_x = self.angles.x * -1;
512
513     dodgemove   = steerlib_traceavoid(0.35,1000);
514     swarmmove   = steerlib_flock(500,75,700,500);
515     reprellmove = steerlib_repell(self.owner.enemy.origin+self.enemy.velocity,2000) * 700;
516
517     if(vlen(dodgemove) == 0)
518     {
519         self.pos1 = steerlib_wander(0.5,0.1,self.pos1);
520         wandermove  = self.pos1 * 50;
521     }
522     else
523         self.pos1 = normalize(self.velocity);
524
525     dodgemove = dodgemove * vlen(self.velocity) * 5;
526
527     newmove = swarmmove + reprellmove + wandermove + dodgemove;
528     self.velocity = movelib_inertmove_byspeed(newmove,300,0.2,0.9);
529     //self.velocity  = movelib_inertmove(dodgemove,0.65);
530
531     self.velocity = movelib_dragvec(0.01,0.6);
532
533     self.angles = vectoangles(self.velocity);
534
535     if(self.health <= 0)
536         flocker_die();
537     else
538         self.nextthink = time + 0.1;
539 }
540
541 MODEL(FLOCKER, "models/turrets/rocket.md3");
542
543 void spawn_flocker()
544 {SELFPARAM();
545     entity flocker;
546
547     flocker = spawn ();
548
549     setorigin(flocker, self.origin + '0 0 32');
550     setmodel (flocker, MDL_FLOCKER);
551     setsize (flocker, '-3 -3 -3', '3 3 3');
552
553     flocker.flock_id   = self.flock_id;
554     flocker.classname  = "flocker";
555     flocker.owner      = self;
556     flocker.think      = flocker_think;
557     flocker.nextthink  = time + random() * 5;
558     PROJECTILE_MAKETRIGGER(flocker);
559     flocker.movetype   = MOVETYPE_BOUNCEMISSILE;
560     flocker.effects    = EF_LOWPRECISION;
561     flocker.velocity   = randomvec() * 300;
562     flocker.angles     = vectoangles(flocker.velocity);
563     flocker.health     = 10;
564     flocker.pos1      = normalize(flocker.velocity + randomvec() * 0.1);
565
566     self.cnt = self.cnt -1;
567
568 }
569
570 void flockerspawn_think()
571 {SELFPARAM();
572
573
574     if(self.cnt > 0)
575         spawn_flocker();
576
577     self.nextthink = time + self.delay;
578
579 }
580
581 void flocker_hunter_think()
582 {SELFPARAM();
583     vector dodgemove,attractmove,newmove;
584     entity e,ee;
585     float d,bd;
586
587     self.angles_x = self.angles.x * -1;
588     makevectors(self.angles);
589     self.angles_x = self.angles.x * -1;
590
591     if(self.enemy)
592     if(vlen(self.enemy.origin - self.origin) < 64)
593     {
594         ee = self.enemy;
595         ee.health = -1;
596         self.enemy = world;
597
598     }
599
600     if(!self.enemy)
601     {
602         e = findchainfloat(flock_id,self.flock_id);
603         while(e)
604         {
605             d = vlen(self.origin - e.origin);
606
607             if(e != self.owner)
608             if(e != ee)
609             if(d > bd)
610             {
611                 self.enemy = e;
612                 bd = d;
613             }
614             e = e.chain;
615         }
616     }
617
618     if(self.enemy)
619         attractmove = steerlib_attract(self.enemy.origin+self.enemy.velocity * 0.1,5000) * 1250;
620     else
621         attractmove = normalize(self.velocity) * 200;
622
623     dodgemove = steerlib_traceavoid(0.35,1500) * vlen(self.velocity);
624
625     newmove = dodgemove + attractmove;
626     self.velocity = movelib_inertmove_byspeed(newmove,1250,0.3,0.7);
627     self.velocity = movelib_dragvec(0.01,0.5);
628
629
630     self.angles = vectoangles(self.velocity);
631     self.nextthink = time + 0.1;
632 }
633
634
635 float globflockcnt;
636 spawnfunc(flockerspawn)
637 {SELFPARAM();
638     ++globflockcnt;
639
640     if(!self.cnt)      self.cnt = 20;
641     if(!self.delay)    self.delay = 0.25;
642     if(!self.flock_id) self.flock_id = globflockcnt;
643
644     self.think     = flockerspawn_think;
645     self.nextthink = time + 0.25;
646
647     self.enemy = spawn();
648
649     setmodel(self.enemy, MDL_FLOCKER);
650     setorigin(self.enemy,self.origin + '0 0 768' + (randomvec() * 128));
651
652     self.enemy.classname = "FLock Hunter";
653     self.enemy.scale     = 3;
654     self.enemy.effects   = EF_LOWPRECISION;
655     self.enemy.movetype  = MOVETYPE_BOUNCEMISSILE;
656     PROJECTILE_MAKETRIGGER(self.enemy);
657     self.enemy.think     = flocker_hunter_think;
658     self.enemy.nextthink = time + 10;
659     self.enemy.flock_id  = self.flock_id;
660     self.enemy.owner     = self;
661 }
662 #endif
663
664
665