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