1 .vector steerto;
3 /**
4     Uniform pull towards a point
5 **/
6 vector steerlib_pull(vector point)
7 {
8     return normalize(point - self.origin);
9 }
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;
29     distance = bound(0.001,vlen(self.origin - point),maximal_distance);
30     direction = normalize(point - self.origin);
31     return  direction * (distance / maximal_distance);
32 }
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;
42     distance = bound(0.001,vlen(self.origin - point),maximal_distance);
43     direction = normalize(point - self.origin);
45     return  direction * (1-(distance / maximal_distance));
46 }
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;
54     distance  = bound(0.00001,vlen(self.origin - point),max_distance);
55     direction = normalize(point - self.origin);
57     influense = 1 - (distance / max_distance);
58     influense = min_influense + (influense * (max_influense - min_influense));
60     return  direction * influense;
61 }
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;
71     if(!distance)
72         distance = vlen(self.origin - point);
74     distance = bound(0.001,distance,maximal_distance);
76     target_direction = normalize(point - self.origin);
77     current_direction = normalize(self.velocity);
79     i_target = bound(min_influense,(1-(distance / maximal_distance)),max_influense);
80     i_current = 1 - i_target;
82     // i_target = bound(min_influense,(1-(distance / maximal_distance)),max_influense);
84     string s;
85     s = ftos(i_target);
86     bprint("IT: ",s,"\n");
87     s = ftos(i_current);
88     bprint("IC  : ",s,"\n");
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;
101     distance = bound(0.001,vlen(self.origin - point),maximal_distance);
102     direction = normalize(self.origin - point);
104     return  direction * (1-(distance / maximal_distance));
105 }
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;
115     distance = vlen(self.origin - point);
118     if(distance < ideal_distance)
119     {
120         direction = normalize(self.origin - point);
121         return direction * (distance / ideal_distance);
122     }
124     direction = normalize(point - self.origin);
125     return direction * (ideal_distance / distance);
127 }
129 /**
130     A random heading in a forward halfcicrle
132     use like:
133     self.target = steerlib_wander(256,32,self.target)
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;
142     if (vlen(wander_point) > tresh)
143         return oldpoint;
145     range = bound(0,range,1);
147     wander_point = self.origin + v_forward * 128;
148     wander_point = wander_point + randomvec() * (range * 128) - randomvec() * (range * 128);
150     return normalize(wander_point - self.origin);
151 }
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;
160     distance = max(vlen(self.origin - point),min_distance);
161     if (min_distance < distance)
162         return '0 0 0';
164     return dodge_dir * (min_distance/distance);
165 }
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;
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 }
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;
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     }
217     push_z = 0;
218     pull_z = 0;
220     return push + (pull * (1 / ccount));
221 }
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.
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;
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     }
248     center = center * (1 / ccount);
249     force = force + (steerlib_arrive(center,radius) * swarm_force);
251     return force;
252 }
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;
267     v_left = v_right * -1;
268     v_down = v_up * -1;
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;
274     //te_lightning1(world,self.origin, trace_endpos);
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;
280     //te_lightning1(world,self.origin, trace_endpos);
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;
286     //te_lightning1(world,self.origin, trace_endpos);
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;
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);
298     return (upwish+leftwish+downwish+rightwish) * 0.25;
300 }
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;
312     v_left = v_right * -1;
315     vt_front = v_forward * length;
316     traceline(self.origin + vofs, self.origin + vofs + vt_front,MOVE_NOMONSTERS,self);
317     f_front = trace_fraction;
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;
323     //te_lightning1(world,self.origin, trace_endpos);
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;
329     //te_lightning1(world,self.origin, trace_endpos);
331     leftwish  = v_left    * f_left;
332     rightwish = v_right   * f_right;
333     frontwish = v_forward * f_front;
335     return normalize(leftwish + rightwish + frontwish);
336 }
339 {
340     float pc,pc2;
342     if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
343         return 1;
345     pc  = pointcontents(point);
346     pc2 = pointcontents(point - '0 0 1');
348     switch(pc)
349     {
350         case CONTENT_SOLID: break;
351         case CONTENT_SLIME: break;
352         case CONTENT_LAVA:  break;
354         case CONTENT_SKY:
355             return 1;
357         case CONTENT_EMPTY:
358             if (pc2 == CONTENT_SOLID)
359                 return 0;
361             if (pc2 == CONTENT_WATER)
362                 if(waterok)
363                     return 0;
365             break;
367         case CONTENT_WATER:
368             if(waterok)
369                 return 0;
371             break;
372     }
374     return 1;
375 }
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;
383     u = '0 0 1' * step_up;
384     d = '0 0 1' * step_down;
386     traceline(from + u, from - d,MOVE_NORMAL,self);
387     if(trace_fraction == 1.0)
388         return 0;
391         return 0;
393     a = trace_endpos;
394     for(i = 0; i < length; i += step)
395     {
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;
402         traceline(b + u, b - d,MOVE_NORMAL,self);
403         if(trace_fraction == 1.0)
404             return i / length;
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     }
415     return 1;
416 }
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;
423     dir_z *= 0.15;
424     vr = vectoangles(dir);
425     //vr_x *= -1;
427     tracebox(self.origin , self.mins,self.maxs,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     }
437     makevectors(vr);
438     bm_forward = beamsweep(self.origin, v_forward, length, step, step_up, step_down);
440     vr = normalize(v_forward + v_right * 0.125);
441     vl = normalize(v_forward - v_right * 0.125);
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);
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);
453         return v_forward;
454     }
456     p = 2 - p;
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);
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);
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     }
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);
476     bm_forward *= bm_forward;
477     bm_right   *= bm_right;
478     bm_left    *= bm_left;
480     vr = vr * bm_right;
481     vl = vl * bm_left;
483     return normalize(vr + vl);
485 }
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         sound (self, CHAN_PROJECTILE, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
498         pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
500     self.owner.cnt += 1;
501     self.owner = world;
503     self.nextthink = time;
504     self.think = SUB_Remove;
505 }
508 void flocker_think()
509 {
510     vector dodgemove,swarmmove;
511     vector reprellmove,wandermove,newmove;
513     self.angles_x = self.angles_x * -1;
514     makevectors(self.angles);
515     self.angles_x = self.angles_x * -1;
517     dodgemove   = steerlib_traceavoid(0.35,1000);
518     swarmmove   = steerlib_flock(500,75,700,500);
519     reprellmove = steerlib_repell(self.owner.enemy.origin+self.enemy.velocity,2000) * 700;
521     if(vlen(dodgemove) == 0)
522     {
523         self.pos1 = steerlib_wander(0.5,0.1,self.pos1);
524         wandermove  = self.pos1 * 50;
525     }
526     else
527         self.pos1 = normalize(self.velocity);
529     dodgemove = dodgemove * vlen(self.velocity) * 5;
531     newmove = swarmmove + reprellmove + wandermove + dodgemove;
532     self.velocity = movelib_inertmove_byspeed(newmove,300,0.2,0.9);
533     //self.velocity  = movelib_inertmove(dodgemove,0.65);
535     self.velocity = movelib_dragvec(0.01,0.6);
537     self.angles = vectoangles(self.velocity);
539     if(self.health <= 0)
540         flocker_die();
541     else
542         self.nextthink = time + 0.1;
543 }
546 void spawn_flocker()
547 {
548     entity flocker;
550     flocker = spawn ();
552     setorigin(flocker, self.origin + '0 0 32');
553     setmodel (flocker, "models/turrets/rocket.md3");
554     setsize (flocker, '-3 -3 -3', '3 3 3');
556     flocker.flock_id   = self.flock_id;
557     flocker.classname  = "flocker";
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);
569     self.cnt = self.cnt -1;
571 }
573 void flockerspawn_think()
574 {
577     if(self.cnt > 0)
578         spawn_flocker();
580     self.nextthink = time + self.delay;
582 }
584 void flocker_hunter_think()
585 {
586     vector dodgemove,attractmove,newmove;
587     entity e,ee;
588     float d,bd;
590     self.angles_x = self.angles_x * -1;
591     makevectors(self.angles);
592     self.angles_x = self.angles_x * -1;
594     if(self.enemy)
595     if(vlen(self.enemy.origin - self.origin) < 64)
596     {
597         ee = self.enemy;
598         ee.health = -1;
599         self.enemy = world;
601     }
603     if(!self.enemy)
604     {
605         e = findchainfloat(flock_id,self.flock_id);
606         while(e)
607         {
608             d = vlen(self.origin - e.origin);
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     }
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;
626     dodgemove = steerlib_traceavoid(0.35,1500) * vlen(self.velocity);
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);
633     self.angles = vectoangles(self.velocity);
634     self.nextthink = time + 0.1;
635 }
638 float globflockcnt;
639 void spawnfunc_flockerspawn()
640 {
641     precache_model ( "models/turrets/rocket.md3");
642     precache_model("models/turrets/c512.md3");
643     ++globflockcnt;
645     if(!self.cnt)      self.cnt = 20;
646     if(!self.delay)    self.delay = 0.25;
647     if(!self.flock_id) self.flock_id = globflockcnt;
649     self.think     = flockerspawn_think;
650     self.nextthink = time + 0.25;
652     self.enemy = spawn();
654     setmodel(self.enemy, "models/turrets/rocket.md3");
655     setorigin(self.enemy,self.origin + '0 0 768' + (randomvec() * 128));
657     self.enemy.classname = "FLock Hunter";
658     self.enemy.scale     = 3;
659     self.enemy.effects   = EF_LOWPRECISION;
660     self.enemy.movetype  = MOVETYPE_BOUNCEMISSILE;
661     PROJECTILE_MAKETRIGGER(self.enemy);
662     self.enemy.think     = flocker_hunter_think;
663     self.enemy.nextthink = time + 10;
664     self.enemy.flock_id  = self.flock_id;
665     self.enemy.owner     = self;
666 }
667 #endif