]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/bot/lib/steerlib/steerlib.qc
Bots: move supporting code into bot directory
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / bot / lib / steerlib / steerlib.qc
diff --git a/qcsrc/server/bot/lib/steerlib/steerlib.qc b/qcsrc/server/bot/lib/steerlib/steerlib.qc
new file mode 100644 (file)
index 0000000..23b8473
--- /dev/null
@@ -0,0 +1,665 @@
+/**
+    Uniform pull towards a point
+**/
+vector steerlib_pull(vector point)
+{SELFPARAM();
+    return normalize(point - self.origin);
+}
+
+/**
+    Uniform push from a point
+**/
+#define steerlib_push(point) normalize(self.origin - point)
+/*
+vector steerlib_push(vector point)
+{
+    return normalize(self.origin - point);
+}
+*/
+/**
+    Pull toward a point, The further away, the stronger the pull.
+**/
+vector steerlib_arrive(vector point,float maximal_distance)
+{SELFPARAM();
+    float distance;
+    vector direction;
+
+    distance = bound(0.001,vlen(self.origin - point),maximal_distance);
+    direction = normalize(point - self.origin);
+    return  direction * (distance / maximal_distance);
+}
+
+/**
+    Pull toward a point increasing the pull the closer we get
+**/
+vector steerlib_attract(vector point, float maximal_distance)
+{SELFPARAM();
+    float distance;
+    vector direction;
+
+    distance = bound(0.001,vlen(self.origin - point),maximal_distance);
+    direction = normalize(point - self.origin);
+
+    return  direction * (1-(distance / maximal_distance));
+}
+
+vector steerlib_attract2(vector point, float min_influense,float max_distance,float max_influense)
+{SELFPARAM();
+    float distance;
+    vector direction;
+    float influense;
+
+    distance  = bound(0.00001,vlen(self.origin - point),max_distance);
+    direction = normalize(point - self.origin);
+
+    influense = 1 - (distance / max_distance);
+    influense = min_influense + (influense * (max_influense - min_influense));
+
+    return  direction * influense;
+}
+
+/*
+vector steerlib_attract2(vector point, float maximal_distance,float min_influense,float max_influense,float distance)
+{
+    //float distance;
+    vector current_direction;
+    vector target_direction;
+    float i_target,i_current;
+
+    if(!distance)
+        distance = vlen(self.origin - point);
+
+    distance = bound(0.001,distance,maximal_distance);
+
+    target_direction = normalize(point - self.origin);
+    current_direction = normalize(self.velocity);
+
+    i_target = bound(min_influense,(1-(distance / maximal_distance)),max_influense);
+    i_current = 1 - i_target;
+
+    // i_target = bound(min_influense,(1-(distance / maximal_distance)),max_influense);
+
+    string s;
+    s = ftos(i_target);
+    bprint("IT: ",s,"\n");
+    s = ftos(i_current);
+    bprint("IC  : ",s,"\n");
+
+    return  normalize((target_direction * i_target) + (current_direction * i_current));
+}
+*/
+/**
+    Move away from a point.
+**/
+vector steerlib_repell(vector point,float maximal_distance)
+{SELFPARAM();
+    float distance;
+    vector direction;
+
+    distance = bound(0.001,vlen(self.origin - point),maximal_distance);
+    direction = normalize(self.origin - point);
+
+    return  direction * (1-(distance / maximal_distance));
+}
+
+/**
+    Try to keep at ideal_distance away from point
+**/
+vector steerlib_standoff(vector point,float ideal_distance)
+{SELFPARAM();
+    float distance;
+    vector direction;
+
+    distance = vlen(self.origin - point);
+
+
+    if(distance < ideal_distance)
+    {
+        direction = normalize(self.origin - point);
+        return direction * (distance / ideal_distance);
+    }
+
+    direction = normalize(point - self.origin);
+    return direction * (ideal_distance / distance);
+
+}
+
+/**
+    A random heading in a forward halfcicrle
+
+    use like:
+    self.target = steerlib_wander(256,32,self.target)
+
+    where range is the cicrle radius and tresh is how close we need to be to pick a new heading.
+**/
+vector steerlib_wander(float range,float tresh,vector oldpoint)
+{SELFPARAM();
+    vector wander_point;
+    wander_point = v_forward - oldpoint;
+
+    if (vlen(wander_point) > tresh)
+        return oldpoint;
+
+    range = bound(0,range,1);
+
+    wander_point = self.origin + v_forward * 128;
+    wander_point = wander_point + randomvec() * (range * 128) - randomvec() * (range * 128);
+
+    return normalize(wander_point - self.origin);
+}
+
+/**
+    Dodge a point. dont work to well.
+**/
+vector steerlib_dodge(vector point,vector dodge_dir,float min_distance)
+{SELFPARAM();
+    float distance;
+
+    distance = max(vlen(self.origin - point),min_distance);
+    if (min_distance < distance)
+        return '0 0 0';
+
+    return dodge_dir * (min_distance/distance);
+}
+
+/**
+    flocking by .flock_id
+    Group will move towards the unified direction while keeping close to eachother.
+**/
+.float flock_id;
+vector steerlib_flock(float _radius, float standoff,float separation_force,float flock_force)
+{SELFPARAM();
+    entity flock_member;
+    vector push = '0 0 0', pull = '0 0 0';
+    float ccount = 0;
+
+    flock_member = findradius(self.origin, _radius);
+    while(flock_member)
+    {
+        if(flock_member != self)
+        if(flock_member.flock_id == self.flock_id)
+        {
+            ++ccount;
+            push = push + (steerlib_repell(flock_member.origin,standoff) * separation_force);
+            pull = pull + (steerlib_arrive(flock_member.origin + flock_member.velocity, _radius) * flock_force);
+        }
+        flock_member = flock_member.chain;
+    }
+    return push + (pull* (1 / ccount));
+}
+
+/**
+    flocking by .flock_id
+    Group will move towards the unified direction while keeping close to eachother.
+    xy only version (for ground movers).
+**/
+vector steerlib_flock2d(float _radius, float standoff,float separation_force,float flock_force)
+{SELFPARAM();
+    entity flock_member;
+    vector push = '0 0 0', pull = '0 0 0';
+    float ccount = 0;
+
+    flock_member = findradius(self.origin,_radius);
+    while(flock_member)
+    {
+        if(flock_member != self)
+        if(flock_member.flock_id == self.flock_id)
+        {
+            ++ccount;
+            push = push + (steerlib_repell(flock_member.origin, standoff) * separation_force);
+            pull = pull + (steerlib_arrive(flock_member.origin + flock_member.velocity, _radius) * flock_force);
+        }
+        flock_member = flock_member.chain;
+    }
+
+    push.z = 0;
+    pull.z = 0;
+
+    return push + (pull * (1 / ccount));
+}
+
+/**
+    All members want to be in the center, and keep away from eachother.
+    The furtehr form the center the more they want to be there.
+
+    This results in a aligned movement (?!) much like flocking.
+**/
+vector steerlib_swarm(float _radius, float standoff,float separation_force,float swarm_force)
+{SELFPARAM();
+    entity swarm_member;
+    vector force = '0 0 0', center = '0 0 0';
+    float ccount = 0;
+
+    swarm_member = findradius(self.origin,_radius);
+
+    while(swarm_member)
+    {
+        if(swarm_member.flock_id == self.flock_id)
+        {
+            ++ccount;
+            center = center + swarm_member.origin;
+            force = force + (steerlib_repell(swarm_member.origin,standoff) * separation_force);
+        }
+        swarm_member = swarm_member.chain;
+    }
+
+    center = center * (1 / ccount);
+    force = force + (steerlib_arrive(center,_radius) * swarm_force);
+
+    return force;
+}
+
+/**
+    Steer towards the direction least obstructed.
+    Run four tracelines in a forward funnel, bias each diretion negative if something is found there.
+    You need to call makevectors() (or equivalent) before this function to set v_forward and v_right
+**/
+vector steerlib_traceavoid(float pitch,float length)
+{SELFPARAM();
+    vector vup_left,vup_right,vdown_left,vdown_right;
+    float fup_left,fup_right,fdown_left,fdown_right;
+    vector upwish,downwish,leftwish,rightwish;
+    vector v_left,v_down;
+
+
+    v_left = v_right * -1;
+    v_down = v_up * -1;
+
+    vup_left = (v_forward + (v_left * pitch + v_up * pitch)) * length;
+    traceline(self.origin, self.origin +  vup_left,MOVE_NOMONSTERS,self);
+    fup_left = trace_fraction;
+
+    //te_lightning1(world,self.origin, trace_endpos);
+
+    vup_right = (v_forward + (v_right * pitch + v_up * pitch)) * length;
+    traceline(self.origin,self.origin + vup_right ,MOVE_NOMONSTERS,self);
+    fup_right = trace_fraction;
+
+    //te_lightning1(world,self.origin, trace_endpos);
+
+    vdown_left = (v_forward + (v_left * pitch + v_down * pitch)) * length;
+    traceline(self.origin,self.origin + vdown_left,MOVE_NOMONSTERS,self);
+    fdown_left = trace_fraction;
+
+    //te_lightning1(world,self.origin, trace_endpos);
+
+    vdown_right = (v_forward + (v_right * pitch + v_down * pitch)) * length;
+    traceline(self.origin,self.origin + vdown_right,MOVE_NOMONSTERS,self);
+    fdown_right = trace_fraction;
+
+    //te_lightning1(world,self.origin, trace_endpos);
+    upwish    = v_up    * (fup_left   + fup_right);
+    downwish  = v_down  * (fdown_left + fdown_right);
+    leftwish  = v_left  * (fup_left   + fdown_left);
+    rightwish = v_right * (fup_right  + fdown_right);
+
+    return (upwish+leftwish+downwish+rightwish) * 0.25;
+
+}
+
+/**
+    Steer towards the direction least obstructed.
+    Run tracelines in a forward trident, bias each direction negative if something is found there.
+**/
+vector steerlib_traceavoid_flat(float pitch, float length, vector vofs)
+{SELFPARAM();
+    vector vt_left, vt_right,vt_front;
+    float f_left, f_right,f_front;
+    vector leftwish, rightwish,frontwish, v_left;
+
+    v_left = v_right * -1;
+
+
+    vt_front = v_forward * length;
+    traceline(self.origin + vofs, self.origin + vofs + vt_front,MOVE_NOMONSTERS,self);
+    f_front = trace_fraction;
+
+    vt_left = (v_forward + (v_left * pitch)) * length;
+    traceline(self.origin + vofs, self.origin + vofs + vt_left,MOVE_NOMONSTERS,self);
+    f_left = trace_fraction;
+
+    //te_lightning1(world,self.origin, trace_endpos);
+
+    vt_right = (v_forward + (v_right * pitch)) * length;
+    traceline(self.origin + vofs, self.origin + vofs + vt_right ,MOVE_NOMONSTERS,self);
+    f_right = trace_fraction;
+
+    //te_lightning1(world,self.origin, trace_endpos);
+
+    leftwish  = v_left    * f_left;
+    rightwish = v_right   * f_right;
+    frontwish = v_forward * f_front;
+
+    return normalize(leftwish + rightwish + frontwish);
+}
+
+float beamsweep_badpoint(vector point,float waterok)
+{
+    float pc,pc2;
+
+    if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
+        return 1;
+
+    pc  = pointcontents(point);
+    pc2 = pointcontents(point - '0 0 1');
+
+    switch(pc)
+    {
+        case CONTENT_SOLID: break;
+        case CONTENT_SLIME: break;
+        case CONTENT_LAVA:  break;
+
+        case CONTENT_SKY:
+            return 1;
+
+        case CONTENT_EMPTY:
+            if (pc2 == CONTENT_SOLID)
+                return 0;
+
+            if (pc2 == CONTENT_WATER)
+                if(waterok)
+                    return 0;
+
+            break;
+
+        case CONTENT_WATER:
+            if(waterok)
+                return 0;
+
+            break;
+    }
+
+    return 1;
+}
+
+//#define BEAMSTEER_VISUAL
+float beamsweep(vector from, vector dir,float length, float step,float step_up, float step_down)
+{SELFPARAM();
+    float i;
+    vector a,b,u,d;
+
+    u = '0 0 1' * step_up;
+    d = '0 0 1' * step_down;
+
+    traceline(from + u, from - d,MOVE_NORMAL,self);
+    if(trace_fraction == 1.0)
+        return 0;
+
+    if(beamsweep_badpoint(trace_endpos,0))
+        return 0;
+
+    a = trace_endpos;
+    for(i = 0; i < length; i += step)
+    {
+
+        b = a + dir * step;
+        tracebox(a + u,'-4 -4 -4','4 4 4', b + u,MOVE_NORMAL,self);
+        if(trace_fraction != 1.0)
+            return i / length;
+
+        traceline(b + u, b - d,MOVE_NORMAL,self);
+        if(trace_fraction == 1.0)
+            return i / length;
+
+        if(beamsweep_badpoint(trace_endpos,0))
+            return i / length;
+#ifdef BEAMSTEER_VISUAL
+        te_lightning1(world,a+u,b+u);
+        te_lightning1(world,b+u,b-d);
+#endif
+        a = trace_endpos;
+    }
+
+    return 1;
+}
+
+vector steerlib_beamsteer(vector dir, float length, float step, float step_up, float step_down)
+{SELFPARAM();
+    float bm_forward, bm_right, bm_left,p;
+    vector vr,vl;
+
+    dir.z *= 0.15;
+    vr = vectoangles(dir);
+    //vr_x *= -1;
+
+    tracebox(self.origin + '0 0 1' * step_up, self.mins, self.maxs, ('0 0 1' * step_up) + self.origin +  (dir * length), MOVE_NOMONSTERS, self);
+    if(trace_fraction == 1.0)
+    {
+        //te_lightning1(self,self.origin,self.origin +  (dir * length));
+        return dir;
+    }
+
+
+
+
+    makevectors(vr);
+    bm_forward = beamsweep(self.origin, v_forward, length, step, step_up, step_down);
+
+    vr = normalize(v_forward + v_right * 0.125);
+    vl = normalize(v_forward - v_right * 0.125);
+
+    bm_right = beamsweep(self.origin, vr, length, step, step_up, step_down);
+    bm_left  = beamsweep(self.origin, vl, length, step, step_up, step_down);
+
+
+    p = bm_left + bm_right;
+    if(p == 2)
+    {
+        //te_lightning1(self,self.origin + '0 0 32',self.origin + '0 0 32' + vr * length);
+        //te_lightning1(self.tur_head,self.origin + '0 0 32',self.origin + '0 0 32' + vl * length);
+
+        return v_forward;
+    }
+
+    p = 2 - p;
+
+    vr = normalize(v_forward + v_right * p);
+    vl = normalize(v_forward - v_right * p);
+    bm_right = beamsweep(self.origin, vr, length, step, step_up, step_down);
+    bm_left  = beamsweep(self.origin, vl, length, step, step_up, step_down);
+
+
+    if(bm_left + bm_right < 0.15)
+    {
+        vr = normalize((v_forward*-1) + v_right * 0.75);
+        vl = normalize((v_forward*-1) - v_right * 0.75);
+
+        bm_right = beamsweep(self.origin, vr, length, step, step_up, step_down);
+        bm_left  = beamsweep(self.origin, vl, length, step, step_up, step_down);
+    }
+
+    //te_lightning1(self,self.origin + '0 0 32',self.origin + '0 0 32' + vr * length);
+    //te_lightning1(self.tur_head,self.origin + '0 0 32',self.origin + '0 0 32' + vl * length);
+
+    bm_forward *= bm_forward;
+    bm_right   *= bm_right;
+    bm_left    *= bm_left;
+
+    vr = vr * bm_right;
+    vl = vl * bm_left;
+
+    return normalize(vr + vl);
+
+}
+
+
+//////////////////////////////////////////////
+//     Testting                             //
+// Everything below this point is a mess :D //
+//////////////////////////////////////////////
+//#define TLIBS_TETSLIBS
+#ifdef TLIBS_TETSLIBS
+void flocker_die()
+{SELFPARAM();
+       Send_Effect(EFFECT_ROCKET_EXPLODE, self.origin, '0 0 0', 1);
+
+    self.owner.cnt += 1;
+    self.owner = world;
+
+    self.nextthink = time;
+    self.think = SUB_Remove;
+}
+
+
+void flocker_think()
+{SELFPARAM();
+    vector dodgemove,swarmmove;
+    vector reprellmove,wandermove,newmove;
+
+    self.angles_x = self.angles.x * -1;
+    makevectors(self.angles);
+    self.angles_x = self.angles.x * -1;
+
+    dodgemove   = steerlib_traceavoid(0.35,1000);
+    swarmmove   = steerlib_flock(500,75,700,500);
+    reprellmove = steerlib_repell(self.owner.enemy.origin+self.enemy.velocity,2000) * 700;
+
+    if(vlen(dodgemove) == 0)
+    {
+        self.pos1 = steerlib_wander(0.5,0.1,self.pos1);
+        wandermove  = self.pos1 * 50;
+    }
+    else
+        self.pos1 = normalize(self.velocity);
+
+    dodgemove = dodgemove * vlen(self.velocity) * 5;
+
+    newmove = swarmmove + reprellmove + wandermove + dodgemove;
+    self.velocity = movelib_inertmove_byspeed(newmove,300,0.2,0.9);
+    //self.velocity  = movelib_inertmove(dodgemove,0.65);
+
+    self.velocity = movelib_dragvec(0.01,0.6);
+
+    self.angles = vectoangles(self.velocity);
+
+    if(self.health <= 0)
+        flocker_die();
+    else
+        self.nextthink = time + 0.1;
+}
+
+MODEL(FLOCKER, "models/turrets/rocket.md3");
+
+void spawn_flocker()
+{SELFPARAM();
+    entity flocker;
+
+    flocker = spawn ();
+
+    setorigin(flocker, self.origin + '0 0 32');
+    setmodel (flocker, MDL_FLOCKER);
+    setsize (flocker, '-3 -3 -3', '3 3 3');
+
+    flocker.flock_id   = self.flock_id;
+    flocker.classname  = "flocker";
+    flocker.owner      = self;
+    flocker.think      = flocker_think;
+    flocker.nextthink  = time + random() * 5;
+    PROJECTILE_MAKETRIGGER(flocker);
+    flocker.movetype   = MOVETYPE_BOUNCEMISSILE;
+    flocker.effects    = EF_LOWPRECISION;
+    flocker.velocity   = randomvec() * 300;
+    flocker.angles     = vectoangles(flocker.velocity);
+    flocker.health     = 10;
+    flocker.pos1      = normalize(flocker.velocity + randomvec() * 0.1);
+
+    self.cnt = self.cnt -1;
+
+}
+
+void flockerspawn_think()
+{SELFPARAM();
+
+
+    if(self.cnt > 0)
+        spawn_flocker();
+
+    self.nextthink = time + self.delay;
+
+}
+
+void flocker_hunter_think()
+{SELFPARAM();
+    vector dodgemove,attractmove,newmove;
+    entity e,ee;
+    float d,bd;
+
+    self.angles_x = self.angles.x * -1;
+    makevectors(self.angles);
+    self.angles_x = self.angles.x * -1;
+
+    if(self.enemy)
+    if(vlen(self.enemy.origin - self.origin) < 64)
+    {
+        ee = self.enemy;
+        ee.health = -1;
+        self.enemy = world;
+
+    }
+
+    if(!self.enemy)
+    {
+        e = findchainfloat(flock_id,self.flock_id);
+        while(e)
+        {
+            d = vlen(self.origin - e.origin);
+
+            if(e != self.owner)
+            if(e != ee)
+            if(d > bd)
+            {
+                self.enemy = e;
+                bd = d;
+            }
+            e = e.chain;
+        }
+    }
+
+    if(self.enemy)
+        attractmove = steerlib_attract(self.enemy.origin+self.enemy.velocity * 0.1,5000) * 1250;
+    else
+        attractmove = normalize(self.velocity) * 200;
+
+    dodgemove = steerlib_traceavoid(0.35,1500) * vlen(self.velocity);
+
+    newmove = dodgemove + attractmove;
+    self.velocity = movelib_inertmove_byspeed(newmove,1250,0.3,0.7);
+    self.velocity = movelib_dragvec(0.01,0.5);
+
+
+    self.angles = vectoangles(self.velocity);
+    self.nextthink = time + 0.1;
+}
+
+
+float globflockcnt;
+spawnfunc(flockerspawn)
+{SELFPARAM();
+    ++globflockcnt;
+
+    if(!self.cnt)      self.cnt = 20;
+    if(!self.delay)    self.delay = 0.25;
+    if(!self.flock_id) self.flock_id = globflockcnt;
+
+    self.think     = flockerspawn_think;
+    self.nextthink = time + 0.25;
+
+    self.enemy = spawn();
+
+    setmodel(self.enemy, MDL_FLOCKER);
+    setorigin(self.enemy,self.origin + '0 0 768' + (randomvec() * 128));
+
+    self.enemy.classname = "FLock Hunter";
+    self.enemy.scale     = 3;
+    self.enemy.effects   = EF_LOWPRECISION;
+    self.enemy.movetype  = MOVETYPE_BOUNCEMISSILE;
+    PROJECTILE_MAKETRIGGER(self.enemy);
+    self.enemy.think     = flocker_hunter_think;
+    self.enemy.nextthink = time + 10;
+    self.enemy.flock_id  = self.flock_id;
+    self.enemy.owner     = self;
+}
+#endif
+
+
+