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