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