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