1 /**
2     Uniform pull towards a point
3 **/
4 vector steerlib_pull(vector point)
5 {SELFPARAM();
6     return normalize(point - self.origin);
7 }
9 /**
10     Uniform push from a point
11 **/
12 #define steerlib_push(point) normalize(self.origin - point)
13 /*
14 vector steerlib_push(vector point)
15 {
16     return normalize(self.origin - point);
17 }
18 */
19 /**
20     Pull toward a point, The further away, the stronger the pull.
21 **/
22 vector steerlib_arrive(vector point,float maximal_distance)
23 {SELFPARAM();
24     float distance;
25     vector direction;
27     distance = bound(0.001,vlen(self.origin - point),maximal_distance);
28     direction = normalize(point - self.origin);
29     return  direction * (distance / maximal_distance);
30 }
32 /**
33     Pull toward a point increasing the pull the closer we get
34 **/
35 vector steerlib_attract(vector point, float maximal_distance)
36 {SELFPARAM();
37     float distance;
38     vector direction;
40     distance = bound(0.001,vlen(self.origin - point),maximal_distance);
41     direction = normalize(point - self.origin);
43     return  direction * (1-(distance / maximal_distance));
44 }
46 vector steerlib_attract2(vector point, float min_influense,float max_distance,float max_influense)
47 {SELFPARAM();
48     float distance;
49     vector direction;
50     float influense;
52     distance  = bound(0.00001,vlen(self.origin - point),max_distance);
53     direction = normalize(point - self.origin);
55     influense = 1 - (distance / max_distance);
56     influense = min_influense + (influense * (max_influense - min_influense));
58     return  direction * influense;
59 }
61 /*
62 vector steerlib_attract2(vector point, float maximal_distance,float min_influense,float max_influense,float distance)
63 {
64     //float distance;
65     vector current_direction;
66     vector target_direction;
67     float i_target,i_current;
69     if(!distance)
70         distance = vlen(self.origin - point);
72     distance = bound(0.001,distance,maximal_distance);
74     target_direction = normalize(point - self.origin);
75     current_direction = normalize(self.velocity);
77     i_target = bound(min_influense,(1-(distance / maximal_distance)),max_influense);
78     i_current = 1 - i_target;
80     // i_target = bound(min_influense,(1-(distance / maximal_distance)),max_influense);
82     string s;
83     s = ftos(i_target);
84     bprint("IT: ",s,"\n");
85     s = ftos(i_current);
86     bprint("IC  : ",s,"\n");
88     return  normalize((target_direction * i_target) + (current_direction * i_current));
89 }
90 */
91 /**
92     Move away from a point.
93 **/
94 vector steerlib_repell(vector point,float maximal_distance)
95 {SELFPARAM();
96     float distance;
97     vector direction;
99     distance = bound(0.001,vlen(self.origin - point),maximal_distance);
100     direction = normalize(self.origin - point);
102     return  direction * (1-(distance / maximal_distance));
103 }
105 /**
106     Try to keep at ideal_distance away from point
107 **/
108 vector steerlib_standoff(vector point,float ideal_distance)
109 {SELFPARAM();
110     float distance;
111     vector direction;
113     distance = vlen(self.origin - point);
116     if(distance < ideal_distance)
117     {
118         direction = normalize(self.origin - point);
119         return direction * (distance / ideal_distance);
120     }
122     direction = normalize(point - self.origin);
123     return direction * (ideal_distance / distance);
125 }
127 /**
128     A random heading in a forward halfcicrle
130     use like:
131     self.target = steerlib_wander(256,32,self.target)
133     where range is the cicrle radius and tresh is how close we need to be to pick a new heading.
134 **/
135 vector steerlib_wander(float range,float tresh,vector oldpoint)
136 {SELFPARAM();
137     vector wander_point;
138     wander_point = v_forward - oldpoint;
140     if (vlen(wander_point) > tresh)
141         return oldpoint;
143     range = bound(0,range,1);
145     wander_point = self.origin + v_forward * 128;
146     wander_point = wander_point + randomvec() * (range * 128) - randomvec() * (range * 128);
148     return normalize(wander_point - self.origin);
149 }
151 /**
152     Dodge a point. dont work to well.
153 **/
154 vector steerlib_dodge(vector point,vector dodge_dir,float min_distance)
155 {SELFPARAM();
156     float distance;
158     distance = max(vlen(self.origin - point),min_distance);
159     if (min_distance < distance)
160         return '0 0 0';
162     return dodge_dir * (min_distance/distance);
163 }
165 /**
166     flocking by .flock_id
167     Group will move towards the unified direction while keeping close to eachother.
168 **/
169 .float flock_id;
170 vector steerlib_flock(float _radius, float standoff,float separation_force,float flock_force)
171 {SELFPARAM();
172     entity flock_member;
173     vector push = '0 0 0', pull = '0 0 0';
174     float ccount = 0;
177     while(flock_member)
178     {
179         if(flock_member != self)
180         if(flock_member.flock_id == self.flock_id)
181         {
182             ++ccount;
183             push = push + (steerlib_repell(flock_member.origin,standoff) * separation_force);
184             pull = pull + (steerlib_arrive(flock_member.origin + flock_member.velocity, _radius) * flock_force);
185         }
186         flock_member = flock_member.chain;
187     }
188     return push + (pull* (1 / ccount));
189 }
191 /**
192     flocking by .flock_id
193     Group will move towards the unified direction while keeping close to eachother.
194     xy only version (for ground movers).
195 **/
196 vector steerlib_flock2d(float _radius, float standoff,float separation_force,float flock_force)
197 {SELFPARAM();
198     entity flock_member;
199     vector push = '0 0 0', pull = '0 0 0';
200     float ccount = 0;
203     while(flock_member)
204     {
205         if(flock_member != self)
206         if(flock_member.flock_id == self.flock_id)
207         {
208             ++ccount;
209             push = push + (steerlib_repell(flock_member.origin, standoff) * separation_force);
210             pull = pull + (steerlib_arrive(flock_member.origin + flock_member.velocity, _radius) * flock_force);
211         }
212         flock_member = flock_member.chain;
213     }
215     push.z = 0;
216     pull.z = 0;
218     return push + (pull * (1 / ccount));
219 }
221 /**
222     All members want to be in the center, and keep away from eachother.
223     The furtehr form the center the more they want to be there.
225     This results in a aligned movement (?!) much like flocking.
226 **/
227 vector steerlib_swarm(float _radius, float standoff,float separation_force,float swarm_force)
228 {SELFPARAM();
229     entity swarm_member;
230     vector force = '0 0 0', center = '0 0 0';
231     float ccount = 0;
235     while(swarm_member)
236     {
237         if(swarm_member.flock_id == self.flock_id)
238         {
239             ++ccount;
240             center = center + swarm_member.origin;
241             force = force + (steerlib_repell(swarm_member.origin,standoff) * separation_force);
242         }
243         swarm_member = swarm_member.chain;
244     }
246     center = center * (1 / ccount);
247     force = force + (steerlib_arrive(center,_radius) * swarm_force);
249     return force;
250 }
252 /**
253     Steer towards the direction least obstructed.
254     Run four tracelines in a forward funnel, bias each diretion negative if something is found there.
255     You need to call makevectors() (or equivalent) before this function to set v_forward and v_right
256 **/
257 vector steerlib_traceavoid(float pitch,float length)
258 {SELFPARAM();
259     vector vup_left,vup_right,vdown_left,vdown_right;
260     float fup_left,fup_right,fdown_left,fdown_right;
261     vector upwish,downwish,leftwish,rightwish;
262     vector v_left,v_down;
265     v_left = v_right * -1;
266     v_down = v_up * -1;
268     vup_left = (v_forward + (v_left * pitch + v_up * pitch)) * length;
269     traceline(self.origin, self.origin +  vup_left,MOVE_NOMONSTERS,self);
270     fup_left = trace_fraction;
272     //te_lightning1(world,self.origin, trace_endpos);
274     vup_right = (v_forward + (v_right * pitch + v_up * pitch)) * length;
275     traceline(self.origin,self.origin + vup_right ,MOVE_NOMONSTERS,self);
276     fup_right = trace_fraction;
278     //te_lightning1(world,self.origin, trace_endpos);
280     vdown_left = (v_forward + (v_left * pitch + v_down * pitch)) * length;
281     traceline(self.origin,self.origin + vdown_left,MOVE_NOMONSTERS,self);
282     fdown_left = trace_fraction;
284     //te_lightning1(world,self.origin, trace_endpos);
286     vdown_right = (v_forward + (v_right * pitch + v_down * pitch)) * length;
287     traceline(self.origin,self.origin + vdown_right,MOVE_NOMONSTERS,self);
288     fdown_right = trace_fraction;
290     //te_lightning1(world,self.origin, trace_endpos);
291     upwish    = v_up    * (fup_left   + fup_right);
292     downwish  = v_down  * (fdown_left + fdown_right);
293     leftwish  = v_left  * (fup_left   + fdown_left);
294     rightwish = v_right * (fup_right  + fdown_right);
296     return (upwish+leftwish+downwish+rightwish) * 0.25;
298 }
300 /**
301     Steer towards the direction least obstructed.
302     Run tracelines in a forward trident, bias each direction negative if something is found there.
303 **/
304 vector steerlib_traceavoid_flat(float pitch, float length, vector vofs)
305 {SELFPARAM();
306     vector vt_left, vt_right,vt_front;
307     float f_left, f_right,f_front;
308     vector leftwish, rightwish,frontwish, v_left;
310     v_left = v_right * -1;
313     vt_front = v_forward * length;
314     traceline(self.origin + vofs, self.origin + vofs + vt_front,MOVE_NOMONSTERS,self);
315     f_front = trace_fraction;
317     vt_left = (v_forward + (v_left * pitch)) * length;
318     traceline(self.origin + vofs, self.origin + vofs + vt_left,MOVE_NOMONSTERS,self);
319     f_left = trace_fraction;
321     //te_lightning1(world,self.origin, trace_endpos);
323     vt_right = (v_forward + (v_right * pitch)) * length;
324     traceline(self.origin + vofs, self.origin + vofs + vt_right ,MOVE_NOMONSTERS,self);
325     f_right = trace_fraction;
327     //te_lightning1(world,self.origin, trace_endpos);
329     leftwish  = v_left    * f_left;
330     rightwish = v_right   * f_right;
331     frontwish = v_forward * f_front;
333     return normalize(leftwish + rightwish + frontwish);
334 }
337 {
338     float pc,pc2;
340     if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
341         return 1;
343     pc  = pointcontents(point);
344     pc2 = pointcontents(point - '0 0 1');
346     switch(pc)
347     {
348         case CONTENT_SOLID: break;
349         case CONTENT_SLIME: break;
350         case CONTENT_LAVA:  break;
352         case CONTENT_SKY:
353             return 1;
355         case CONTENT_EMPTY:
356             if (pc2 == CONTENT_SOLID)
357                 return 0;
359             if (pc2 == CONTENT_WATER)
360                 if(waterok)
361                     return 0;
363             break;
365         case CONTENT_WATER:
366             if(waterok)
367                 return 0;
369             break;
370     }
372     return 1;
373 }
375 //#define BEAMSTEER_VISUAL
376 float beamsweep(vector from, vector dir,float length, float step,float step_up, float step_down)
377 {SELFPARAM();
378     float i;
379     vector a,b,u,d;
381     u = '0 0 1' * step_up;
382     d = '0 0 1' * step_down;
384     traceline(from + u, from - d,MOVE_NORMAL,self);
385     if(trace_fraction == 1.0)
386         return 0;
389         return 0;
391     a = trace_endpos;
392     for(i = 0; i < length; i += step)
393     {
395         b = a + dir * step;
396         tracebox(a + u,'-4 -4 -4','4 4 4', b + u,MOVE_NORMAL,self);
397         if(trace_fraction != 1.0)
398             return i / length;
400         traceline(b + u, b - d,MOVE_NORMAL,self);
401         if(trace_fraction == 1.0)
402             return i / length;
405             return i / length;
406 #ifdef BEAMSTEER_VISUAL
407         te_lightning1(world,a+u,b+u);
408         te_lightning1(world,b+u,b-d);
409 #endif
410         a = trace_endpos;
411     }
413     return 1;
414 }
416 vector steerlib_beamsteer(vector dir, float length, float step, float step_up, float step_down)
417 {SELFPARAM();
418     float bm_forward, bm_right, bm_left,p;
419     vector vr,vl;
421     dir.z *= 0.15;
422     vr = vectoangles(dir);
423     //vr_x *= -1;
425     tracebox(self.origin + '0 0 1' * step_up, self.mins, self.maxs, ('0 0 1' * step_up) + self.origin +  (dir * length), MOVE_NOMONSTERS, self);
426     if(trace_fraction == 1.0)
427     {
428         //te_lightning1(self,self.origin,self.origin +  (dir * length));
429         return dir;
430     }
435     makevectors(vr);
436     bm_forward = beamsweep(self.origin, v_forward, length, step, step_up, step_down);
438     vr = normalize(v_forward + v_right * 0.125);
439     vl = normalize(v_forward - v_right * 0.125);
441     bm_right = beamsweep(self.origin, vr, length, step, step_up, step_down);
442     bm_left  = beamsweep(self.origin, vl, length, step, step_up, step_down);
445     p = bm_left + bm_right;
446     if(p == 2)
447     {
448         //te_lightning1(self,self.origin + '0 0 32',self.origin + '0 0 32' + vr * length);
449         //te_lightning1(self.tur_head,self.origin + '0 0 32',self.origin + '0 0 32' + vl * length);
451         return v_forward;
452     }
454     p = 2 - p;
456     vr = normalize(v_forward + v_right * p);
457     vl = normalize(v_forward - v_right * p);
458     bm_right = beamsweep(self.origin, vr, length, step, step_up, step_down);
459     bm_left  = beamsweep(self.origin, vl, length, step, step_up, step_down);
462     if(bm_left + bm_right < 0.15)
463     {
464         vr = normalize((v_forward*-1) + v_right * 0.75);
465         vl = normalize((v_forward*-1) - v_right * 0.75);
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     }
471     //te_lightning1(self,self.origin + '0 0 32',self.origin + '0 0 32' + vr * length);
472     //te_lightning1(self.tur_head,self.origin + '0 0 32',self.origin + '0 0 32' + vl * length);
474     bm_forward *= bm_forward;
475     bm_right   *= bm_right;
476     bm_left    *= bm_left;
478     vr = vr * bm_right;
479     vl = vl * bm_left;
481     return normalize(vr + vl);
483 }
486 //////////////////////////////////////////////
487 //     Testting                             //
488 // Everything below this point is a mess :D //
489 //////////////////////////////////////////////
490 //#define TLIBS_TETSLIBS
491 #ifdef TLIBS_TETSLIBS
492 void flocker_die()
493 {SELFPARAM();
494         Send_Effect(EFFECT_ROCKET_EXPLODE, self.origin, '0 0 0', 1);
496     self.owner.cnt += 1;
497     self.owner = world;
499     self.nextthink = time;
500     self.think = SUB_Remove;
501 }
504 void flocker_think()
505 {SELFPARAM();
506     vector dodgemove,swarmmove;
507     vector reprellmove,wandermove,newmove;
509     self.angles_x = self.angles.x * -1;
510     makevectors(self.angles);
511     self.angles_x = self.angles.x * -1;
513     dodgemove   = steerlib_traceavoid(0.35,1000);
514     swarmmove   = steerlib_flock(500,75,700,500);
515     reprellmove = steerlib_repell(self.owner.enemy.origin+self.enemy.velocity,2000) * 700;
517     if(vlen(dodgemove) == 0)
518     {
519         self.pos1 = steerlib_wander(0.5,0.1,self.pos1);
520         wandermove  = self.pos1 * 50;
521     }
522     else
523         self.pos1 = normalize(self.velocity);
525     dodgemove = dodgemove * vlen(self.velocity) * 5;
527     newmove = swarmmove + reprellmove + wandermove + dodgemove;
528     self.velocity = movelib_inertmove_byspeed(newmove,300,0.2,0.9);
529     //self.velocity  = movelib_inertmove(dodgemove,0.65);
531     self.velocity = movelib_dragvec(0.01,0.6);
533     self.angles = vectoangles(self.velocity);
535     if(self.health <= 0)
536         flocker_die();
537     else
538         self.nextthink = time + 0.1;
539 }
541 MODEL(FLOCKER, "models/turrets/rocket.md3");
543 void spawn_flocker()
544 {SELFPARAM();
545     entity flocker;
547     flocker = spawn ();
549     setorigin(flocker, self.origin + '0 0 32');
550     setmodel (flocker, MDL_FLOCKER);
551     setsize (flocker, '-3 -3 -3', '3 3 3');
553     flocker.flock_id   = self.flock_id;
554     flocker.classname  = "flocker";
555     flocker.owner      = self;
556     flocker.think      = 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);
566     self.cnt = self.cnt -1;
568 }
570 void flockerspawn_think()
571 {SELFPARAM();
574     if(self.cnt > 0)
575         spawn_flocker();
577     self.nextthink = time + self.delay;
579 }
581 void flocker_hunter_think()
582 {SELFPARAM();
583     vector dodgemove,attractmove,newmove;
584     entity e,ee;
585     float d,bd;
587     self.angles_x = self.angles.x * -1;
588     makevectors(self.angles);
589     self.angles_x = self.angles.x * -1;
591     if(self.enemy)
592     if(vlen(self.enemy.origin - self.origin) < 64)
593     {
594         ee = self.enemy;
595         ee.health = -1;
596         self.enemy = world;
598     }
600     if(!self.enemy)
601     {
602         e = findchainfloat(flock_id,self.flock_id);
603         while(e)
604         {
605             d = vlen(self.origin - e.origin);
607             if(e != self.owner)
608             if(e != ee)
609             if(d > bd)
610             {
611                 self.enemy = e;
612                 bd = d;
613             }
614             e = e.chain;
615         }
616     }
618     if(self.enemy)
619         attractmove = steerlib_attract(self.enemy.origin+self.enemy.velocity * 0.1,5000) * 1250;
620     else
621         attractmove = normalize(self.velocity) * 200;
623     dodgemove = steerlib_traceavoid(0.35,1500) * vlen(self.velocity);
625     newmove = dodgemove + attractmove;
626     self.velocity = movelib_inertmove_byspeed(newmove,1250,0.3,0.7);
627     self.velocity = movelib_dragvec(0.01,0.5);
630     self.angles = vectoangles(self.velocity);
631     self.nextthink = time + 0.1;
632 }
635 float globflockcnt;
636 spawnfunc(flockerspawn)
637 {SELFPARAM();
638     ++globflockcnt;
640     if(!self.cnt)      self.cnt = 20;
641     if(!self.delay)    self.delay = 0.25;
642     if(!self.flock_id) self.flock_id = globflockcnt;
644     self.think     = flockerspawn_think;
645     self.nextthink = time + 0.25;
647     self.enemy = spawn();
649     setmodel(self.enemy, MDL_FLOCKER);
650     setorigin(self.enemy,self.origin + '0 0 768' + (randomvec() * 128));
652     self.enemy.classname = "FLock Hunter";
653     self.enemy.scale     = 3;
654     self.enemy.effects   = EF_LOWPRECISION;
655     self.enemy.movetype  = MOVETYPE_BOUNCEMISSILE;
656     PROJECTILE_MAKETRIGGER(self.enemy);
657     self.enemy.think     = flocker_hunter_think;
658     self.enemy.nextthink = time + 10;
659     self.enemy.flock_id  = self.flock_id;
660     self.enemy.owner     = self;
661 }
662 #endif