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