]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/steerlib.qc
Merge branch 'master' into terencehill/translate_colors_2
[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(vector point)
11 {SELFPARAM();
12     return normalize(point - self.origin);
13 }
14
15 /**
16     Uniform push from a point
17 **/
18 #define steerlib_push(point) normalize(self.origin - point)
19 /*
20 vector steerlib_push(vector point)
21 {
22     return normalize(self.origin - point);
23 }
24 */
25 /**
26     Pull toward a point, The further away, the stronger the pull.
27 **/
28 vector steerlib_arrive(vector point,float maximal_distance)
29 {SELFPARAM();
30     float distance;
31     vector direction;
32
33     distance = bound(0.001,vlen(self.origin - point),maximal_distance);
34     direction = normalize(point - self.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(vector point, float maximal_distance)
42 {SELFPARAM();
43     float distance;
44     vector direction;
45
46     distance = bound(0.001,vlen(self.origin - point),maximal_distance);
47     direction = normalize(point - self.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(self.origin - point);
77
78     distance = bound(0.001,distance,maximal_distance);
79
80     target_direction = normalize(point - self.origin);
81     current_direction = normalize(self.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(vector point,float maximal_distance)
101 {SELFPARAM();
102     float distance;
103     vector direction;
104
105     distance = bound(0.001,vlen(self.origin - point),maximal_distance);
106     direction = normalize(self.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(vector point,float ideal_distance)
115 {SELFPARAM();
116     float distance;
117     vector direction;
118
119     distance = vlen(self.origin - point);
120
121
122     if(distance < ideal_distance)
123     {
124         direction = normalize(self.origin - point);
125         return direction * (distance / ideal_distance);
126     }
127
128     direction = normalize(point - self.origin);
129     return direction * (ideal_distance / distance);
130
131 }
132
133 /**
134     A random heading in a forward halfcicrle
135
136     use like:
137     self.target = steerlib_wander(256,32,self.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(float range,float tresh,vector oldpoint)
142 {SELFPARAM();
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 = self.origin + v_forward * 128;
152     wander_point = wander_point + randomvec() * (range * 128) - randomvec() * (range * 128);
153
154     return normalize(wander_point - self.origin);
155 }
156
157 /**
158     Dodge a point. dont work to well.
159 **/
160 vector steerlib_dodge(vector point,vector dodge_dir,float min_distance)
161 {SELFPARAM();
162     float distance;
163
164     distance = max(vlen(self.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(float _radius, float standoff,float separation_force,float flock_force)
177 {SELFPARAM();
178     entity flock_member;
179     vector push = '0 0 0', pull = '0 0 0';
180     float ccount = 0;
181
182     flock_member = findradius(self.origin, _radius);
183     while(flock_member)
184     {
185         if(flock_member != self)
186         if(flock_member.flock_id == self.flock_id)
187         {
188             ++ccount;
189             push = push + (steerlib_repell(flock_member.origin,standoff) * separation_force);
190             pull = pull + (steerlib_arrive(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(float _radius, float standoff,float separation_force,float flock_force)
203 {SELFPARAM();
204     entity flock_member;
205     vector push = '0 0 0', pull = '0 0 0';
206     float ccount = 0;
207
208     flock_member = findradius(self.origin,_radius);
209     while(flock_member)
210     {
211         if(flock_member != self)
212         if(flock_member.flock_id == self.flock_id)
213         {
214             ++ccount;
215             push = push + (steerlib_repell(flock_member.origin, standoff) * separation_force);
216             pull = pull + (steerlib_arrive(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(float _radius, float standoff,float separation_force,float swarm_force)
234 {SELFPARAM();
235     entity swarm_member;
236     vector force = '0 0 0', center = '0 0 0';
237     float ccount = 0;
238
239     swarm_member = findradius(self.origin,_radius);
240
241     while(swarm_member)
242     {
243         if(swarm_member.flock_id == self.flock_id)
244         {
245             ++ccount;
246             center = center + swarm_member.origin;
247             force = force + (steerlib_repell(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(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(float pitch,float length)
264 {SELFPARAM();
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(self.origin, self.origin +  vup_left,MOVE_NOMONSTERS,self);
276     fup_left = trace_fraction;
277
278     //te_lightning1(world,self.origin, trace_endpos);
279
280     vup_right = (v_forward + (v_right * pitch + v_up * pitch)) * length;
281     traceline(self.origin,self.origin + vup_right ,MOVE_NOMONSTERS,self);
282     fup_right = trace_fraction;
283
284     //te_lightning1(world,self.origin, trace_endpos);
285
286     vdown_left = (v_forward + (v_left * pitch + v_down * pitch)) * length;
287     traceline(self.origin,self.origin + vdown_left,MOVE_NOMONSTERS,self);
288     fdown_left = trace_fraction;
289
290     //te_lightning1(world,self.origin, trace_endpos);
291
292     vdown_right = (v_forward + (v_right * pitch + v_down * pitch)) * length;
293     traceline(self.origin,self.origin + vdown_right,MOVE_NOMONSTERS,self);
294     fdown_right = trace_fraction;
295
296     //te_lightning1(world,self.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(float pitch, float length, vector vofs)
311 {SELFPARAM();
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(self.origin + vofs, self.origin + vofs + vt_front,MOVE_NOMONSTERS,self);
321     f_front = trace_fraction;
322
323     vt_left = (v_forward + (v_left * pitch)) * length;
324     traceline(self.origin + vofs, self.origin + vofs + vt_left,MOVE_NOMONSTERS,self);
325     f_left = trace_fraction;
326
327     //te_lightning1(world,self.origin, trace_endpos);
328
329     vt_right = (v_forward + (v_right * pitch)) * length;
330     traceline(self.origin + vofs, self.origin + vofs + vt_right ,MOVE_NOMONSTERS,self);
331     f_right = trace_fraction;
332
333     //te_lightning1(world,self.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(vector from, vector dir,float length, float step,float step_up, float step_down)
383 {SELFPARAM();
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,self);
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,self);
403         if(trace_fraction != 1.0)
404             return i / length;
405
406         traceline(b + u, b - d,MOVE_NORMAL,self);
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(vector dir, float length, float step, float step_up, float step_down)
423 {SELFPARAM();
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(self.origin + '0 0 1' * step_up, self.mins, self.maxs, ('0 0 1' * step_up) + self.origin +  (dir * length), MOVE_NOMONSTERS, self);
432     if(trace_fraction == 1.0)
433     {
434         //te_lightning1(self,self.origin,self.origin +  (dir * length));
435         return dir;
436     }
437
438
439
440
441     makevectors(vr);
442     bm_forward = beamsweep(self.origin, v_forward, length, step, step_up, step_down);
443
444     vr = normalize(v_forward + v_right * 0.125);
445     vl = normalize(v_forward - v_right * 0.125);
446
447     bm_right = beamsweep(self.origin, vr, length, step, step_up, step_down);
448     bm_left  = beamsweep(self.origin, vl, length, step, step_up, step_down);
449
450
451     p = bm_left + bm_right;
452     if(p == 2)
453     {
454         //te_lightning1(self,self.origin + '0 0 32',self.origin + '0 0 32' + vr * length);
455         //te_lightning1(self.tur_head,self.origin + '0 0 32',self.origin + '0 0 32' + vl * length);
456
457         return v_forward;
458     }
459
460     p = 2 - p;
461
462     vr = normalize(v_forward + v_right * p);
463     vl = normalize(v_forward - v_right * p);
464     bm_right = beamsweep(self.origin, vr, length, step, step_up, step_down);
465     bm_left  = beamsweep(self.origin, vl, length, step, step_up, step_down);
466
467
468     if(bm_left + bm_right < 0.15)
469     {
470         vr = normalize((v_forward*-1) + v_right * 0.75);
471         vl = normalize((v_forward*-1) - v_right * 0.75);
472
473         bm_right = beamsweep(self.origin, vr, length, step, step_up, step_down);
474         bm_left  = beamsweep(self.origin, vl, length, step, step_up, step_down);
475     }
476
477     //te_lightning1(self,self.origin + '0 0 32',self.origin + '0 0 32' + vr * length);
478     //te_lightning1(self.tur_head,self.origin + '0 0 32',self.origin + '0 0 32' + vl * length);
479
480     bm_forward *= bm_forward;
481     bm_right   *= bm_right;
482     bm_left    *= bm_left;
483
484     vr = vr * bm_right;
485     vl = vl * bm_left;
486
487     return normalize(vr + vl);
488
489 }
490
491
492 //////////////////////////////////////////////
493 //     Testting                             //
494 // Everything below this point is a mess :D //
495 //////////////////////////////////////////////
496 //#define TLIBS_TETSLIBS
497 #ifdef TLIBS_TETSLIBS
498 void flocker_die()
499 {SELFPARAM();
500         Send_Effect(EFFECT_ROCKET_EXPLODE, self.origin, '0 0 0', 1);
501
502     self.owner.cnt += 1;
503     self.owner = world;
504
505     self.nextthink = time;
506     self.think = SUB_Remove;
507 }
508
509
510 void flocker_think()
511 {SELFPARAM();
512     vector dodgemove,swarmmove;
513     vector reprellmove,wandermove,newmove;
514
515     self.angles_x = self.angles.x * -1;
516     makevectors(self.angles);
517     self.angles_x = self.angles.x * -1;
518
519     dodgemove   = steerlib_traceavoid(0.35,1000);
520     swarmmove   = steerlib_flock(500,75,700,500);
521     reprellmove = steerlib_repell(self.owner.enemy.origin+self.enemy.velocity,2000) * 700;
522
523     if(vlen(dodgemove) == 0)
524     {
525         self.pos1 = steerlib_wander(0.5,0.1,self.pos1);
526         wandermove  = self.pos1 * 50;
527     }
528     else
529         self.pos1 = normalize(self.velocity);
530
531     dodgemove = dodgemove * vlen(self.velocity) * 5;
532
533     newmove = swarmmove + reprellmove + wandermove + dodgemove;
534     self.velocity = movelib_inertmove_byspeed(newmove,300,0.2,0.9);
535     //self.velocity  = movelib_inertmove(dodgemove,0.65);
536
537     self.velocity = movelib_dragvec(0.01,0.6);
538
539     self.angles = vectoangles(self.velocity);
540
541     if(self.health <= 0)
542         flocker_die();
543     else
544         self.nextthink = time + 0.1;
545 }
546
547 MODEL(FLOCKER, "models/turrets/rocket.md3");
548
549 void spawn_flocker()
550 {SELFPARAM();
551     entity flocker = new(flocker);
552
553     setorigin(flocker, self.origin + '0 0 32');
554     setmodel (flocker, MDL_FLOCKER);
555     setsize (flocker, '-3 -3 -3', '3 3 3');
556
557     flocker.flock_id   = self.flock_id;
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 {SELFPARAM();
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 {SELFPARAM();
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(vdist(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 spawnfunc(flockerspawn)
640 {SELFPARAM();
641     ++globflockcnt;
642
643     if(!self.cnt)      self.cnt = 20;
644     if(!self.delay)    self.delay = 0.25;
645     if(!self.flock_id) self.flock_id = globflockcnt;
646
647     self.think     = flockerspawn_think;
648     self.nextthink = time + 0.25;
649
650     self.enemy = new(FLock Hunter);
651
652     setmodel(self.enemy, MDL_FLOCKER);
653     setorigin(self.enemy,self.origin + '0 0 768' + (randomvec() * 128));
654
655     self.enemy.scale     = 3;
656     self.enemy.effects   = EF_LOWPRECISION;
657     self.enemy.movetype  = MOVETYPE_BOUNCEMISSILE;
658     PROJECTILE_MAKETRIGGER(self.enemy);
659     self.enemy.think     = flocker_hunter_think;
660     self.enemy.nextthink = time + 10;
661     self.enemy.flock_id  = self.flock_id;
662     self.enemy.owner     = self;
663 }
664 #endif
665
666
667