]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/steerlib.qc
Fix up some #ifdef'd code
[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(world,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(world,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(world,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(world,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(world,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(world,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 float beamsweep_badpoint(vector point,float waterok)
343 {
344     float pc,pc2;
345
346     if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
347         return 1;
348
349     pc  = pointcontents(point);
350     pc2 = pointcontents(point - '0 0 1');
351
352     switch(pc)
353     {
354         case CONTENT_SOLID: break;
355         case CONTENT_SLIME: break;
356         case CONTENT_LAVA:  break;
357
358         case CONTENT_SKY:
359             return 1;
360
361         case CONTENT_EMPTY:
362             if (pc2 == CONTENT_SOLID)
363                 return 0;
364
365             if (pc2 == CONTENT_WATER)
366                 if(waterok)
367                     return 0;
368
369             break;
370
371         case CONTENT_WATER:
372             if(waterok)
373                 return 0;
374
375             break;
376     }
377
378     return 1;
379 }
380
381 //#define BEAMSTEER_VISUAL
382 float beamsweep(entity this, vector from, vector dir,float length, float step,float step_up, float step_down)
383 {
384     float i;
385     vector a,b,u,d;
386
387     u = '0 0 1' * step_up;
388     d = '0 0 1' * step_down;
389
390     traceline(from + u, from - d,MOVE_NORMAL,this);
391     if(trace_fraction == 1.0)
392         return 0;
393
394     if(beamsweep_badpoint(trace_endpos,0))
395         return 0;
396
397     a = trace_endpos;
398     for(i = 0; i < length; i += step)
399     {
400
401         b = a + dir * step;
402         tracebox(a + u,'-4 -4 -4','4 4 4', b + u,MOVE_NORMAL,this);
403         if(trace_fraction != 1.0)
404             return i / length;
405
406         traceline(b + u, b - d,MOVE_NORMAL,this);
407         if(trace_fraction == 1.0)
408             return i / length;
409
410         if(beamsweep_badpoint(trace_endpos,0))
411             return i / length;
412 #ifdef BEAMSTEER_VISUAL
413         te_lightning1(world,a+u,b+u);
414         te_lightning1(world,b+u,b-d);
415 #endif
416         a = trace_endpos;
417     }
418
419     return 1;
420 }
421
422 vector steerlib_beamsteer(entity this, vector dir, float length, float step, float step_up, float step_down)
423 {
424     float bm_forward, bm_right, bm_left,p;
425     vector vr,vl;
426
427     dir.z *= 0.15;
428     vr = vectoangles(dir);
429     //vr_x *= -1;
430
431     tracebox(this.origin + '0 0 1' * step_up, this.mins, this.maxs, ('0 0 1' * step_up) + this.origin +  (dir * length), MOVE_NOMONSTERS, this);
432     if(trace_fraction == 1.0)
433     {
434         //te_lightning1(this,this.origin,this.origin +  (dir * length));
435         return dir;
436     }
437
438     makevectors(vr);
439     bm_forward = beamsweep(this, this.origin, v_forward, length, step, step_up, step_down);
440
441     vr = normalize(v_forward + v_right * 0.125);
442     vl = normalize(v_forward - v_right * 0.125);
443
444     bm_right = beamsweep(this, this.origin, vr, length, step, step_up, step_down);
445     bm_left  = beamsweep(this, this.origin, vl, length, step, step_up, step_down);
446
447
448     p = bm_left + bm_right;
449     if(p == 2)
450     {
451         //te_lightning1(this,this.origin + '0 0 32',this.origin + '0 0 32' + vr * length);
452         //te_lightning1(this.tur_head,this.origin + '0 0 32',this.origin + '0 0 32' + vl * length);
453
454         return v_forward;
455     }
456
457     p = 2 - p;
458
459     vr = normalize(v_forward + v_right * p);
460     vl = normalize(v_forward - v_right * p);
461     bm_right = beamsweep(this, this.origin, vr, length, step, step_up, step_down);
462     bm_left  = beamsweep(this, this.origin, vl, length, step, step_up, step_down);
463
464
465     if(bm_left + bm_right < 0.15)
466     {
467         vr = normalize((v_forward*-1) + v_right * 0.75);
468         vl = normalize((v_forward*-1) - v_right * 0.75);
469
470         bm_right = beamsweep(this, this.origin, vr, length, step, step_up, step_down);
471         bm_left  = beamsweep(this, this.origin, vl, length, step, step_up, step_down);
472     }
473
474     //te_lightning1(this,this.origin + '0 0 32',this.origin + '0 0 32' + vr * length);
475     //te_lightning1(this.tur_head,this.origin + '0 0 32',this.origin + '0 0 32' + vl * length);
476
477     bm_forward *= bm_forward;
478     bm_right   *= bm_right;
479     bm_left    *= bm_left;
480
481     vr = vr * bm_right;
482     vl = vl * bm_left;
483
484     return normalize(vr + vl);
485
486 }
487
488
489 //////////////////////////////////////////////
490 //     Testting                             //
491 // Everything below this point is a mess :D //
492 //////////////////////////////////////////////
493 //#define TLIBS_TETSLIBS
494 #ifdef TLIBS_TETSLIBS
495 void flocker_die(entity this)
496 {
497         Send_Effect(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1);
498
499     this.owner.cnt += 1;
500     this.owner = world;
501
502     this.nextthink = time;
503     setthink(this, SUB_Remove);
504 }
505
506
507 void flocker_think(entity this)
508 {
509     vector dodgemove,swarmmove;
510     vector reprellmove,wandermove,newmove;
511
512     this.angles_x = this.angles.x * -1;
513     makevectors(this.angles);
514     this.angles_x = this.angles.x * -1;
515
516     dodgemove   = steerlib_traceavoid(this, 0.35,1000);
517     swarmmove   = steerlib_flock(this, 500,75,700,500);
518     reprellmove = steerlib_repell(this, this.owner.enemy.origin+this.enemy.velocity,2000) * 700;
519
520     if(dodgemove == '0 0 0')
521     {
522         this.pos1 = steerlib_wander(this, 0.5,0.1,this.pos1);
523         wandermove  = this.pos1 * 50;
524     }
525     else
526         this.pos1 = normalize(this.velocity);
527
528     dodgemove = dodgemove * vlen(this.velocity) * 5;
529
530     newmove = swarmmove + reprellmove + wandermove + dodgemove;
531     this.velocity = movelib_inertmove_byspeed(this, newmove,300,0.2,0.9);
532     //this.velocity  = movelib_inertmove(this, dodgemove,0.65);
533
534     this.velocity = movelib_dragvec(this, 0.01,0.6);
535
536     this.angles = vectoangles(this.velocity);
537
538     if(this.health <= 0)
539         flocker_die(this);
540     else
541         this.nextthink = time + 0.1;
542 }
543
544 MODEL(FLOCKER, "models/turrets/rocket.md3");
545
546 void spawn_flocker(entity this)
547 {
548     entity flocker = new(flocker);
549
550     setorigin(flocker, this.origin + '0 0 32');
551     setmodel (flocker, MDL_FLOCKER);
552     setsize (flocker, '-3 -3 -3', '3 3 3');
553
554     flocker.flock_id   = this.flock_id;
555     flocker.owner      = this;
556     setthink(flocker, 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     this.cnt = this.cnt -1;
567
568 }
569
570 void flockerspawn_think(entity this)
571 {
572     if(this.cnt > 0)
573         spawn_flocker(this);
574
575     this.nextthink = time + this.delay;
576
577 }
578
579 void flocker_hunter_think(entity this)
580 {
581     vector dodgemove,attractmove,newmove;
582     entity e,ee;
583     float d,bd;
584
585     this.angles_x = this.angles.x * -1;
586     makevectors(this.angles);
587     this.angles_x = this.angles.x * -1;
588
589     if(this.enemy)
590     if(vdist(this.enemy.origin - this.origin, <, 64))
591     {
592         ee = this.enemy;
593         ee.health = -1;
594         this.enemy = world;
595
596     }
597
598     if(!this.enemy)
599     {
600         e = findchainfloat(flock_id,this.flock_id);
601         while(e)
602         {
603             d = vlen(this.origin - e.origin);
604
605             if(e != this.owner)
606             if(e != ee)
607             if(d > bd)
608             {
609                 this.enemy = e;
610                 bd = d;
611             }
612             e = e.chain;
613         }
614     }
615
616     if(this.enemy)
617         attractmove = steerlib_attract(this, this.enemy.origin+this.enemy.velocity * 0.1,5000) * 1250;
618     else
619         attractmove = normalize(this.velocity) * 200;
620
621     dodgemove = steerlib_traceavoid(this, 0.35,1500) * vlen(this.velocity);
622
623     newmove = dodgemove + attractmove;
624     this.velocity = movelib_inertmove_byspeed(this, newmove,1250,0.3,0.7);
625     this.velocity = movelib_dragvec(this, 0.01,0.5);
626
627
628     this.angles = vectoangles(this.velocity);
629     this.nextthink = time + 0.1;
630 }
631
632
633 float globflockcnt;
634 spawnfunc(flockerspawn)
635 {
636     ++globflockcnt;
637
638     if(!this.cnt)      this.cnt = 20;
639     if(!this.delay)    this.delay = 0.25;
640     if(!this.flock_id) this.flock_id = globflockcnt;
641
642     setthink(this, flockerspawn_think);
643     this.nextthink = time + 0.25;
644
645     this.enemy = new(FLock Hunter);
646
647     setmodel(this.enemy, MDL_FLOCKER);
648     setorigin(this.enemy,this.origin + '0 0 768' + (randomvec() * 128));
649
650     this.enemy.scale     = 3;
651     this.enemy.effects   = EF_LOWPRECISION;
652     this.enemy.movetype  = MOVETYPE_BOUNCEMISSILE;
653     PROJECTILE_MAKETRIGGER(this.enemy);
654     setthink(this.enemy, flocker_hunter_think);
655     this.enemy.nextthink = time + 10;
656     this.enemy.flock_id  = this.flock_id;
657     this.enemy.owner     = this;
658 }
659 #endif
660
661
662