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