]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/steerlib.qc
Merge branch 'master' into martin-t/globals
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / steerlib.qc
1 #include "steerlib.qh"
2 #if defined(CSQC)
3 #elif defined(MENUQC)
4 #elif defined(SVQC)
5     #include "pathlib/utility.qh"
6 #endif
7
8 /**
9     Uniform pull towards a point
10 **/
11 #define steerlib_pull(ent,point) normalize(point - (ent).origin)
12 /*vector steerlib_pull(entity this, vector point)
13 {
14     return normalize(point - this.origin);
15 }*/
16
17 /**
18     Uniform push from a point
19 **/
20 #define steerlib_push(ent,point) normalize((ent).origin - point)
21 /*
22 vector steerlib_push(entity this, vector point)
23 {
24     return normalize(this.origin - point);
25 }
26 */
27 /**
28     Pull toward a point, The further away, the stronger the pull.
29 **/
30 vector steerlib_arrive(entity this, vector point, float maximal_distance)
31 {
32     float distance = bound(0.001, vlen(this.origin - point), maximal_distance);
33     vector direction = normalize(point - this.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(entity this, vector point, float maximal_distance)
41 {
42     float distance = bound(0.001, vlen(this.origin - point), maximal_distance);
43     vector direction = normalize(point - this.origin);
44
45     return  direction * (1 - (distance / maximal_distance));
46 }
47
48 vector steerlib_attract2(entity this, vector point, float min_influense, float max_distance, float max_influense)
49 {
50     float distance = bound(0.00001, vlen(this.origin - point), max_distance);
51     vector direction = normalize(point - this.origin);
52
53     float influense = 1 - (distance / max_distance);
54     influense = min_influense + (influense * (max_influense - min_influense));
55
56     return direction * influense;
57 }
58
59 /*
60 vector steerlib_attract2(vector point, float maximal_distance,float min_influense,float max_influense,float distance)
61 {
62     //float distance;
63     vector current_direction;
64     vector target_direction;
65     float i_target,i_current;
66
67     if(!distance)
68         distance = vlen(this.origin - point);
69
70     distance = bound(0.001,distance,maximal_distance);
71
72     target_direction = normalize(point - this.origin);
73     current_direction = normalize(this.velocity);
74
75     i_target = bound(min_influense,(1-(distance / maximal_distance)),max_influense);
76     i_current = 1 - i_target;
77
78     // i_target = bound(min_influense,(1-(distance / maximal_distance)),max_influense);
79
80     string s;
81     s = ftos(i_target);
82     bprint("IT: ",s,"\n");
83     s = ftos(i_current);
84     bprint("IC  : ",s,"\n");
85
86     return  normalize((target_direction * i_target) + (current_direction * i_current));
87 }
88 */
89 /**
90     Move away from a point.
91 **/
92 vector steerlib_repel(entity this, vector point, float maximal_distance)
93 {
94     float distance = bound(0.001, vlen(this.origin - point), maximal_distance);
95     vector direction = normalize(this.origin - point);
96
97     return  direction * (1 - (distance / maximal_distance));
98 }
99
100 /**
101     Try to keep at ideal_distance away from point
102 **/
103 vector steerlib_standoff(entity this, vector point, float ideal_distance)
104 {
105     vector direction;
106     float distance = vlen(this.origin - point);
107
108     if(distance < ideal_distance)
109     {
110         direction = normalize(this.origin - point);
111         return direction * (distance / ideal_distance);
112     }
113
114     direction = normalize(point - this.origin);
115     return direction * (ideal_distance / distance);
116
117 }
118
119 /**
120     A random heading in a forward semicircle
121
122     usage:
123     this.target = steerlib_wander(256, 32, this.target)
124
125     where range is the circle radius and threshold is how close we need to be to pick a new heading.
126     Assumes v_forward is set by makevectors
127 **/
128 vector steerlib_wander(entity this, float range, float threshold, vector oldpoint)
129 {
130     vector wander_point = v_forward - oldpoint;
131
132     if (vdist(wander_point, >, threshold))
133         return oldpoint;
134
135     range = bound(0, range, 1);
136
137     wander_point = this.origin + v_forward * 128;
138     wander_point = wander_point + randomvec() * (range * 128) - randomvec() * (range * 128);
139
140     return normalize(wander_point - this.origin);
141 }
142
143 /**
144     Dodge a point NOTE: doesn't work well
145 **/
146 vector steerlib_dodge(entity this, vector point, vector dodge_dir, float min_distance)
147 {
148     float distance = max(vlen(this.origin - point), min_distance);
149     if (min_distance < distance)
150         return '0 0 0';
151
152     return dodge_dir * (min_distance / distance);
153 }
154
155 /**
156     flocking by .flock_id
157     Group will move towards the unified direction while keeping close to eachother.
158 **/
159 .float flock_id;
160 vector steerlib_flock(entity this, float _radius, float standoff, float separation_force, float flock_force)
161 {
162     vector push = '0 0 0', pull = '0 0 0';
163     int ccount = 0;
164
165     entity flock_member = findradius(this.origin, _radius);
166     while(flock_member)
167     {
168         if(flock_member != this)
169         if(flock_member.flock_id == this.flock_id)
170         {
171             ++ccount;
172             push = push + (steerlib_repel(this, flock_member.origin,standoff) * separation_force);
173             pull = pull + (steerlib_arrive(this, flock_member.origin + flock_member.velocity, _radius) * flock_force);
174         }
175         flock_member = flock_member.chain;
176     }
177     return push + (pull* (1 / ccount));
178 }
179
180 /**
181     flocking by .flock_id
182     Group will move towards the unified direction while keeping close to eachother.
183     xy only version (for ground movers).
184 **/
185 vector steerlib_flock2d(entity this, float _radius, float standoff, float separation_force, float flock_force)
186 {
187     vector push = '0 0 0', pull = '0 0 0';
188     int ccount = 0;
189
190     entity flock_member = findradius(this.origin,_radius);
191     while(flock_member)
192     {
193         if(flock_member != this)
194         if(flock_member.flock_id == this.flock_id)
195         {
196             ++ccount;
197             push = push + (steerlib_repel(this, flock_member.origin, standoff) * separation_force);
198             pull = pull + (steerlib_arrive(this, flock_member.origin + flock_member.velocity, _radius) * flock_force);
199         }
200         flock_member = flock_member.chain;
201     }
202
203     push.z = 0;
204     pull.z = 0;
205
206     return push + (pull * (1 / ccount));
207 }
208
209 /**
210     All members want to be in the center, and keep away from eachother.
211     The further from the center the more they want to be there.
212
213     This results in a aligned movement (?!) much like flocking.
214 **/
215 vector steerlib_swarm(entity this, float _radius, float standoff, float separation_force, float swarm_force)
216 {
217     vector force = '0 0 0', center = '0 0 0';
218     int ccount = 0;
219
220     entity swarm_member = findradius(this.origin,_radius);
221     while(swarm_member)
222     {
223         if(swarm_member.flock_id == this.flock_id)
224         {
225             ++ccount;
226             center = center + swarm_member.origin;
227             force = force + (steerlib_repel(this, swarm_member.origin,standoff) * separation_force);
228         }
229         swarm_member = swarm_member.chain;
230     }
231
232     center = center * (1 / ccount);
233     force = force + (steerlib_arrive(this, center,_radius) * swarm_force);
234
235     return force;
236 }
237
238 /**
239     Steer towards the direction least obstructed.
240     Run four tracelines in a forward funnel, bias each diretion negative if something is found there.
241     You need to call makevectors() (or equivalent) before this function to set v_forward and v_right
242 **/
243 vector steerlib_traceavoid(entity this, float pitch, float length)
244 {
245     vector v_left = v_right * -1;
246     vector v_down = v_up * -1;
247
248     vector vup_left = (v_forward + (v_left * pitch + v_up * pitch)) * length;
249     traceline(this.origin, this.origin +  vup_left, MOVE_NOMONSTERS, this);
250     float fup_left = trace_fraction;
251
252     //te_lightning1(NULL,this.origin, trace_endpos);
253
254     vector vup_right = (v_forward + (v_right * pitch + v_up * pitch)) * length;
255     traceline(this.origin, this.origin + vup_right, MOVE_NOMONSTERS, this);
256     float fup_right = trace_fraction;
257
258     //te_lightning1(NULL,this.origin, trace_endpos);
259
260     vector vdown_left = (v_forward + (v_left * pitch + v_down * pitch)) * length;
261     traceline(this.origin, this.origin + vdown_left, MOVE_NOMONSTERS, this);
262     float fdown_left = trace_fraction;
263
264     //te_lightning1(NULL,this.origin, trace_endpos);
265
266     vector vdown_right = (v_forward + (v_right * pitch + v_down * pitch)) * length;
267     traceline(this.origin, this.origin + vdown_right, MOVE_NOMONSTERS, this);
268     float fdown_right = trace_fraction;
269
270     //te_lightning1(NULL,this.origin, trace_endpos);
271     vector upwish    = v_up    * (fup_left   + fup_right);
272     vector downwish  = v_down  * (fdown_left + fdown_right);
273     vector leftwish  = v_left  * (fup_left   + fdown_left);
274     vector rightwish = v_right * (fup_right  + fdown_right);
275
276     return (upwish + leftwish + downwish + rightwish) * 0.25;
277
278 }
279
280 /**
281     Steer towards the direction least obstructed.
282     Run tracelines in a forward trident, bias each direction negative if something is found there.
283     You need to call makevectors() (or equivalent) before this function to set v_forward and v_right
284 **/
285 vector steerlib_traceavoid_flat(entity this, float pitch, float length, vector vofs)
286 {
287     vector v_left = v_right * -1;
288
289     vector vt_front = v_forward * length;
290     traceline(this.origin + vofs, this.origin + vofs + vt_front,MOVE_NOMONSTERS,this);
291     float f_front = trace_fraction;
292
293     vector vt_left = (v_forward + (v_left * pitch)) * length;
294     traceline(this.origin + vofs, this.origin + vofs + vt_left,MOVE_NOMONSTERS,this);
295     float f_left = trace_fraction;
296
297     //te_lightning1(NULL,this.origin, trace_endpos);
298
299     vector vt_right = (v_forward + (v_right * pitch)) * length;
300     traceline(this.origin + vofs, this.origin + vofs + vt_right ,MOVE_NOMONSTERS,this);
301     float f_right = trace_fraction;
302
303     //te_lightning1(NULL,this.origin, trace_endpos);
304
305     vector leftwish  = v_left    * f_left;
306     vector rightwish = v_right   * f_right;
307     vector frontwish = v_forward * f_front;
308
309     return normalize(leftwish + rightwish + frontwish);
310 }
311
312 //#define BEAMSTEER_VISUAL
313 float beamsweep(entity this, vector from, vector dir, float length, float step, float step_up, float step_down)
314 {
315     vector u = '0 0 1' * step_up;
316     vector d = '0 0 1' * step_down;
317
318     traceline(from + u, from - d,MOVE_NORMAL,this);
319     if(trace_fraction == 1.0)
320         return 0;
321
322     if(!location_isok(trace_endpos, false, false))
323         return 0;
324
325     vector a = trace_endpos;
326     for(int i = 0; i < length; i += step)
327     {
328
329         vector b = a + dir * step;
330         tracebox(a + u,'-4 -4 -4','4 4 4', b + u,MOVE_NORMAL,this);
331         if(trace_fraction != 1.0)
332             return i / length;
333
334         traceline(b + u, b - d,MOVE_NORMAL,this);
335         if(trace_fraction == 1.0)
336             return i / length;
337
338         if(!location_isok(trace_endpos, false, false))
339             return i / length;
340 #ifdef BEAMSTEER_VISUAL
341         te_lightning1(NULL,a+u,b+u);
342         te_lightning1(NULL,b+u,b-d);
343 #endif
344         a = trace_endpos;
345     }
346
347     return 1;
348 }
349
350 vector steerlib_beamsteer(entity this, vector dir, float length, float step, float step_up, float step_down)
351 {
352     dir.z *= 0.15;
353     vector vr = vectoangles(dir);
354     //vr.x *= -1;
355
356     tracebox(this.origin + '0 0 1' * step_up, this.mins, this.maxs, ('0 0 1' * step_up) + this.origin +  (dir * length), MOVE_NOMONSTERS, this);
357     if(trace_fraction == 1.0)
358     {
359         //te_lightning1(this,this.origin,this.origin + (dir * length));
360         return dir;
361     }
362
363     makevectors(vr);
364     float bm_forward = beamsweep(this, this.origin, v_forward, length, step, step_up, step_down);
365
366     vr = normalize(v_forward + v_right * 0.125);
367     vector vl = normalize(v_forward - v_right * 0.125);
368
369     float bm_right = beamsweep(this, this.origin, vr, length, step, step_up, step_down);
370     float bm_left  = beamsweep(this, this.origin, vl, length, step, step_up, step_down);
371
372     float p = bm_left + bm_right;
373     if(p == 2)
374     {
375         //te_lightning1(this,this.origin + '0 0 32',this.origin + '0 0 32' + vr * length);
376         //te_lightning1(this.tur_head,this.origin + '0 0 32',this.origin + '0 0 32' + vl * length);
377
378         return v_forward;
379     }
380
381     p = 2 - p;
382
383     vr = normalize(v_forward + v_right * p);
384     vl = normalize(v_forward - v_right * p);
385     bm_right = beamsweep(this, this.origin, vr, length, step, step_up, step_down);
386     bm_left  = beamsweep(this, this.origin, vl, length, step, step_up, step_down);
387
388
389     if(bm_left + bm_right < 0.15)
390     {
391         vr = normalize((v_forward*-1) + v_right * 0.90);
392         vl = normalize((v_forward*-1) - v_right * 0.90);
393
394         bm_right = beamsweep(this, this.origin, vr, length, step, step_up, step_down);
395         bm_left  = beamsweep(this, this.origin, vl, length, step, step_up, step_down);
396     }
397
398     //te_lightning1(this,this.origin + '0 0 32',this.origin + '0 0 32' + vr * length);
399     //te_lightning1(this.tur_head,this.origin + '0 0 32',this.origin + '0 0 32' + vl * length);
400
401     bm_forward *= bm_forward;
402     bm_right   *= bm_right;
403     bm_left    *= bm_left;
404
405     vr = vr * bm_right;
406     vl = vl * bm_left;
407
408     return normalize(vr + vl);
409 }