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