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