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