/*   _____________________________________________________________
    /                                                             \
    | File: term.cpp - terminator driver
    \_____________________________________________________________/

    contents:

        code for

            Whisker
            TermDriver


    Developers: ID: Name:
    =========== --- -----
                JTH Juergen Thumm
  _____________________________________/VERSION HISTORY\____________________
 /Date_ Who What____________________________________________________________\
 yymmdd --- -----------------------------------------------------------------
 951207 JTH created
*/

#   include "global.h"
#   include "term.h"

#define CLEAR(x) memset(x,0,sizeof(x))

pcol scansafeline(
    cpix start[2],
    cpix end[2],
    cpix firsthit[2],
    TermDriver *td  = 0,
    cpix *prehitpos = 0
    )
{
    // scan from start to end for first matching color or display border
    long sx = start[0];
    long sy = start[1];
    long ex = end[0];
    long ey = end[1];

    long dx = ex - sx;
    long dy = ey - sy;

    long adx    = abs(dx);
    long ady    = abs(dy);
    long amax   = max(adx, ady);

    long cx=-1,cy=-1,ox,oy;
    pcol col;

    if(amax)
    {
        for(long i=0; i<amax; i++)
        {
            ox  = cx;   // store for use
            oy  = cy;   // as prehitpos

            cx  = sx + i * dx / amax;
            cy  = sy + i * dy / amax;

            if(field.inbord(cx,cy))
            {
                switch(col = field.readpix(cx,cy))
                {
                    // case col_lsword0:
                    // case col_lsword1:
                    //  if(!td || td->enemylscol != col)
                    //      break;  // ignore

                    case col_flame:
                    case col_fire:
                    case col_waveexpl:
                    case col_fixwall:
                    case col_dust:
                    case col_cmimpact:
                    case col_mgimpact:
                        // found a matching color:
                        firsthit[0] = cx;
                        firsthit[1] = cy;
                        if(prehitpos)
                        {
                            // return also pos'n before match:

                            if(ox != -1)
                            {
                                prehitpos[0]    = ox;
                                prehitpos[1]    = oy;
                            }
                            else
                            {
                                prehitpos[0]    = cx;
                                prehitpos[1]    = cy;
                            }
                        }
                        return  col;
                }
            }
            else
                return -1;  // illegal coord reached
        }
    }

    // lines with just one pixel are NOT supported
    // and return always -1
    return  -1; // nothing found or illegal coords
}

void xorline(cpix drawpos[4])
{
    if(!(field.opt.debugmode & ZDEB_VIDEODEBUG))
        return;

    // unplot old line
    HDC hdc    = field.getfronthdc();
    int ropold = SetROP2(hdc, R2_NOT);
    SetTextColor(hdc, RGB(255,255,255));
    MoveTo(hdc, drawpos[0], drawpos[1]);
    LineTo(hdc, drawpos[2], drawpos[3]);
    SetROP2(hdc, ropold);
    memset(drawpos, 0, sizeof(drawpos));
}

void xorline(cpix drawpos1[2],cpix drawpos2[2])
{
    if(!(field.opt.debugmode & ZDEB_VIDEODEBUG))
        return;

    // unplot old line
    HDC hdc    = field.getfronthdc();
    int ropold = SetROP2(hdc, R2_NOT);
    SetTextColor(hdc, RGB(255,255,255));
    MoveTo(hdc, drawpos1[0], drawpos1[1]);
    LineTo(hdc, drawpos2[0], drawpos2[1]);
    SetROP2(hdc, ropold);
}

long calcdist(cpix pos1[2], cpix pos2[2])
{
    long d[3];
    d[0] = pos1[0] - pos2[0];
    d[1] = pos1[1] - pos2[1];
    return sqrt((ulong)(d[0]*d[0])+(ulong)(d[1]*d[1]));
}

rcode calccircpos(cpix base[2], short ang, short rad, cpix targ[2])
{
    targ[0] = base[0] + convif(ftrig.sin(ang) * rad);
    targ[1] = base[1] - convif(ftrig.cos(ang) * rad);
    if(field.inbord(targ))
       return OK;
    return FAILED;
}

//|| Whisker
Whisker::Whisker()
{
    reset();
}

void Whisker::reset(void)
{
    pos[0] =  0; // unset
    pos[1] =  0;
    angle  =  0;
    flags  =  0;
    mdist  = -1; // n/a
    edist  = -1; // n/a
    delay  =  0; // scan on 1st cycle
    CLEAR(vdpos);
}

bool Whisker::scan(cpix xtpos[2], pcol targcol)
{
    shpos[0] = 0;
    shpos[1] = 0;
    if(scansafeline(pos, xtpos, shpos) == -1)
        return 1;

    return 0;
}

//|| TermDriver

TermDriver::TermDriver(class Bike* bike, short xpower)
    :   BikeDriver  (bike),
        PlayObj     (TERMDRIVER, 200),
        power       (xpower)
{_
    safepower   = min(xpower, 100);
    revpower    = 100 - safepower;

    char buf[40];
    sprintf(buf, "%ld power", (long)power);
    bike->tell(buf);

    itype = TERM_DRIVER;

    bike->shields  += power * 5 / 100;

    bike->parkallow = 0;    // parking allowed y/n
    bike->ana.log   = 1;    // analogue joystick is emulated
    shootDelay      = 5000; // initial delay

    if(!field.num.humans)
        shootDelay  = 500;  // two terminators: immediate action

    memset(targ, 0, sizeof(targ));  // reset target infos
    itarg           = -1;   // no target selected

    fdiag = sqrt((ulong)((long)field.size[0]*(long)field.size[0])
                      + (ulong)((long)field.size[1]*(long)field.size[1]));
    if(!fdiag)  fdiag = 100L;   // is used in division

    dirvary     = 0;
    dvdelay     = 0;
    action      = 0;
    actioncnt   = 0;

    ttpos[0]    = 0;
    ttpos[1]    = 0;
    ttang       = 0;
    ttflags     = 0;
    ttwindex    = -1;   // unset
    ttdist      = 0;

    enemylscol  = (bike->number ? col_lsword0 : col_lsword1);

    memset(drawpos, 0, sizeof(drawpos));
}

rcode TermDriver::unsetttpos(void)
{
    if(!ttpos[0])
        return OK;

    CLEAR(ttpos);
    ttang   = 0;

    if(ttwindex == -1)
        return OK;

    if(ttwindex >= WHISKERS)
    {   SysErr(912951558); return INTERNAL; }

    wisk[ttwindex].reset();

    return OK;
}

rcode TermDriver::step(PlayMsg* pm)
{_
    if(pm)  return INDATA;  // no msg support

    ifn(bike)   {SysErr(1207952052); return INTERNAL;}

    // down-count shooting delay.
    if((shootDelay -= cyclesize) < 0)
        shootDelay = 0;

    // down-count direction vary delay.
    if((dvdelay -= cyclesize) < 0)
    {
        dvdelay = 400;
        short v = ftrig.degmax() >> 3;
        dirvary = (rand() % v) - (v>>1);
    }

    // if ttpos is set, did we reach it in the meantime?
    if(ttpos[0])
    {
        cpix dx = bike->pos[0] - ttpos[0];
        cpix dy = bike->pos[1] - ttpos[1];

        if(abs(dx) < 30 && abs(dy) < 30)
            unsetttpos();
    }

    // re-scan whiskers and stuff
    stepwhiskers();

    // display line to ttpos
    if(drawpos[0])
    {
        xorline(drawpos);
        CLEAR(drawpos);
    }

    if(ttpos[0] && ttflags)
    {
        drawpos[0] = bike->pos[0];
        drawpos[1] = bike->pos[1];
        drawpos[2] = ttpos[0];
        drawpos[3] = ttpos[1];
        xorline(drawpos);
    }

    scanfield();

    // create weapons which are missing.

    bool crt = 0;

    // safe and silent mount of a weapon:

#define SAFEMOUNT(x)              \
    if(x && !x->isdead())         \
    {                             \
        field.enableplaysound(0); \
        x->mount(&bike->weapons); \
        field.enableplaysound(1); \
    }                             \
    else                          \
    if(x && x->isdead())          \
        delete x;

    XCharge *xc = 0;
    AddAmmo *aa = 0;

    short nplus = 0;
    short i;

    if(!hasweapon(MGUN))
    {
        nplus   = safepower * 2 / 100;

        MGun      *mg = new MGun(bike->pos);      SAFEMOUNT(mg);
        for(i=0; i<nplus; i++)
        {          xc = new XCharge(bike->pos);   SAFEMOUNT(xc);    }
        AddAmmo   *aa = new AddAmmo(bike->pos);   SAFEMOUNT(aa);
                   aa = new AddAmmo(bike->pos);   SAFEMOUNT(aa);
        crt = 1;
    }

    if(!hasweapon(PGUN))
    {
        nplus   = safepower * 4 / 100;

        PGun      *pg = new PGun(bike->pos);      SAFEMOUNT(pg);
        for(i=0; i<nplus; i++)
        {          xc = new XCharge(bike->pos);   SAFEMOUNT(xc);    }
        AddAmmo   *aa = new AddAmmo(bike->pos);   SAFEMOUNT(aa);
                   aa = new AddAmmo(bike->pos);   SAFEMOUNT(aa);
        crt = 1;
    }

    if(!hasweapon(MLAUNCHER))
    {
        MLauncher *ml = new MLauncher(bike->pos); SAFEMOUNT(ml);
        AddAmmo   *aa = new AddAmmo(bike->pos);   SAFEMOUNT(aa);
                   aa = new AddAmmo(bike->pos);   SAFEMOUNT(aa);
        crt = 1;
    }

    if(crt)
    {
        // a weapon was shot empty. give the victim some time to recover
        // while we re-collect it. with full power, there's no delay.
        long l = revpower * 5000 / 100;
        shootDelay = max(l, shootDelay);
    }

    if(field.opt.debugmode & ZDEB_VIDEODEBUG)
    {
        char buf[80];
        sprintf(buf, "tpos %d %d ang %d f %ld wi %d",ttpos[0],ttpos[1],ttang,ttflags,ttwindex);
        field.tell(buf);
    }

    return OK;
}

int TermDriver::specfunc()
{_
    return 0;
}

bool TermDriver::accelerate()
{_
    if(!bike)   {SysErr(1207951050); return 0;}

    Bike *tbike = enemy();
    if(tbike && tbike->isdead())
        return 0;   // don't shoot, it's dead

    if(action == PUMPINTOWALL)
    {
        action = 0;
        bike->LockTargDir(-1);  // unlock, single shot was done meanwhile
    }

    if(shootDelay)
       return 0;

    if(ttpos[0] && (ttflags & Whisker::ENEMYDIRECTSIGHT))
    {
        // shoot with which weapon?

        if(     ttdist >= field.sizemax / 2
            ||  action == FIREMISSILES
          )
        {
            selectweapon(MLAUNCHER);
            shootDelay  = 200 + revpower * 300 / 100;
            if(--actioncnt < 0)
            {
                actioncnt = 0;
                action    = 0;
            }
        }
        else
        if(ttdist < field.sizemax / 10)
        {
            selectweapon(PGUN);         // pump away
            shootDelay  = 100 + revpower * 300 / 100;
            action      = 0;
            long dist   = calcdist(bike->pos, ttpos);
            bike->LockTargDist(max(10, dist - 30));
            ttpos[0]    = 0;
            ttflags     = 0;
        }
        else
        {
            short rval = rand() % 100;

            if(rval < 10)
            {
                selectweapon(PGUN);         // first stun...
                shootDelay  = 100 + revpower * 400 / 100;
                action      = FIREMISSILES; // ...then fire
                actioncnt   = 5;
                bike->LockTargDist(calcdist(bike->pos, ttpos));
            }
            else
            if(rval < 50 && willpumpenemyintowall())
            {
                selectweapon(PGUN);         // to blast into wall
                action      = PUMPINTOWALL;
                actioncnt   = 0;
                shootDelay  = 100 + revpower * 500 / 100;
                // target dist and dir were locked by willpump...
                field.playsound(SND_TERMFOC1);
            }
            else
            {
                selectweapon(MGUN);
                shootDelay  = 100 + revpower * 500 / 100;
                short precrange = 1 + revpower; // upto so many pix unprecision
                short precvary  = (rand() % precrange) - (precrange>>1);
                long  dist      = calcdist(bike->pos, ttpos) + precvary;
                bike->LockTargDist(max(dist, 30));
            }
        }

        return 1;
    }

    return 0;
}

Bike *TermDriver::enemy(void)
{
    if(!bike) return 0;

    if(bike->number)
        return (Bike*)field.getobj(BIKE0);
    else
        return (Bike*)field.getobj(BIKE1);
}

bool TermDriver::willpumpenemyintowall(void)
{
    if(!bike)  return 0;
    Bike *tbike = enemy();
    if(!tbike) return 0;

    cpix epos[2] = { tbike->pos[0], tbike->pos[1] };
    cpix opos[2] = { 0,0 };
    cpix fpos[2] = { 0,0 };
    cpix hpos[2] = { 0,0 };
    short offang;
    pcol colbnd[2]={col_OBST_MIN+1,col_OBST_MAX-1}; // see TLaser::PlotTSpot

    for(short bail=2+1; --bail;)
    {
        // select a random position 40 pix off enemy;
        // parallel, calc a possible fly target point
        for(short bail2=2+1; --bail2;)
        {
            offang = rand() % ftrig.degmax();
            if(calccircpos(epos, offang, 40, opos))
                continue;
            if(calccircpos(epos, offang + (ftrig.degmax()>>1), 80, fpos))
                continue;
            break;
        }
        if(!bail2) continue;

        // does our target laser really manage to target to 'opos'?
        if(field.scanline(bike->launchpos(), opos, colbnd, hpos) != -1)
            continue; // no, something's in the way

        // now scan from enemy his expected flying track for walls.
        // use 'scansafeline' to scan for an 'unsafe' line.
        if(scansafeline(epos, fpos, hpos) != -1)
        {
            xorline(epos, fpos);    // for visual debugging
            break;    // success
        }
    }
    if(!bail) return 0;

    // so, placing the target laser at 'opos' would blast enemy
    // into an obstacle.
    // get supposed targeting laser angle
    AnalogueCtl ana;
    short targang = ana.CalcAngle(opos[0]-bike->pos[0],opos[1]-bike->pos[1]);
    long  dist    = calcdist(bike->pos,opos);

    if(dist < field.sizemax / 6)    // impact radius, see PExpl
        return 0;   // oh, we would blast ourselves too, so don't do it

    bike->LockTargDir(targang);
    bike->LockTargDist(dist);

    return 1;
}

bool TermDriver::hasweapon(enum ObjCodes what)
{_
    if(!bike)   {SysErr(14079521124); return INTERNAL;}

    for(XWeapon *xw = bike->weapons.first();
        xw && xw->idcode() != what;
        xw = (XWeapon*)xw->next()
       );

    return xw ? 1 : 0;
}

rcode TermDriver::selectweapon(enum ObjCodes what)
{_
    if(!bike)   {SysErr(14079521051); return INTERNAL;}

    XWeapon *xw = 0;

    if(     (xw = bike->weapons.first())
        &&  xw->idcode() == what
      )
        return OK;  // is already at front.

    for(xw = bike->weapons.first();
        xw && xw->idcode() != what;
        xw = (XWeapon*)xw->next()
       );

    if(!xw) return FAILED;

    // put selected weapon to front of list.
    bike->weapons.RemoveXW(xw);
    bike->weapons.AddFrontXW(xw);

    return OK;
}

void TermDriver::scanfield(void)
{_
    if(!bike)   {SysErr(1407952116); return;}

    short i = -1;
    TargInfo *tinf = 0;

    itarg = -1;

    // scan both main bikes for distance to terminator
    // and for visibility.
    for(i=0; i<2; i++)
    {
        tinf = &targ[i];

        if(tinf->bk = (Bike*)field.getobj(i ? BIKE1 : BIKE0))
        {
            tinf->d[0] = tinf->bk->pos[0] - bike->pos[0];
            tinf->d[1] = tinf->bk->pos[1] - bike->pos[1];
            tinf->d[2] = sqrt((ulong)(tinf->d[0]*tinf->d[0])
                            + (ulong)(tinf->d[1]*tinf->d[1]));

            if(scansafeline(bike->pos, tinf->bk->pos, tinf->opos) == -1)
                tinf->insight = 1;
            else
                tinf->insight = 0;
        }
        else
            return; // a main bike's missing (should never happen)
    }

    // select target bike:
    if(bike->number == 1)
    {
        // terminator drives second main bike!
        // always select 1st as target then:
        itarg = 0;
    }
    else
    if(targ[0].insight ^ targ[1].insight)   // XOR!
    {
        // only ONE bike is in sight. which one?
        if(targ[0].insight) itarg = 0;
        if(targ[1].insight) itarg = 1;
    }
    else
    {
        // BOTH or NONE are in sight. take nearest.
        if(targ[0].d[2] < targ[1].d[2]) itarg = 0;
        if(targ[1].d[2] < targ[0].d[2]) itarg = 1;
    }
}

enum Dirs TermDriver::direction(short *vdir)
{_
    if(!bike)   {SysErr(1207952116); return UP;}

    Bike *tbike;
    if(bike->number) tbike = (Bike*)field.getobj(BIKE0);
    else             tbike = (Bike*)field.getobj(BIKE1);

    if(!tbike) return UP;

    long elong;

    if (!field.num.humans)
        elong = 30000L; // fast demo
    else
    {
        long dist   = calcdist(bike->pos, tbike->pos);
        elong  = (dist * 20000L / (field.sizemax>>1)) + 10000L;
        if(elong > 30000L) elong = 30000L;

        if(action == RETREAT)
            elong = 30000L; // retreat fast
        else
            if(convif(bike->speed) < (60 + safepower * 60 / 100))
               elong = 30000L;  // accelerate
    }

    AnalogueCtl ana;
    short atarg = ana.CalcAngle(ttpos[0]-bike->pos[0],ttpos[1]-bike->pos[1]);

    vdir[0] = 0 + convif(ftrig.sin(atarg) * elong);
    vdir[1] = 0 - convif(ftrig.cos(atarg) * elong);

    return field.convdir(field.convdir(vdir[0], vdir[1]));
}

void CreateTerminator(void)
{_
    Bike *bk = new Bike(3);
    ifn(bk) {MemErr(302940933); return;}

    ifn(bk->driver = new TermDriver(bk, 100))
        {MemErr(302940934); return;}

    cpix mp[2] = {field.bord[0] + field.size[0] / 2,
                  field.bord[1] + field.size[1] / 2};

    bk->SetPos(mp);

    field.stopsound();  // attention please
    enum Sounds snd = (enum Sounds)(SND_TERMCRT - 5);
    snd = (enum Sounds)(snd + 5);
    field.playsound(snd, 1);
}

rcode TermDriver::stepwhiskers(void)
{
    if(!bike) return FAILED;

    Whisker *wcur;
    Bike    *tbike;
    cpix     tpos[2], hpos[2];
    short    nmothersighted = 0;
    short    nenemysighted  = 0;
    pcol     ocol = 0;

    if(!(tbike = enemy()))
        return FAILED;

    if(action == RETREAT)
    {
        if((actioncnt -= cyclesize) < 0)
        {
            actioncnt = 0;
            action    = 0;
        }
        else
            return OK;  // continue retreat
    }

    // enemy DIRECTLY in sight?
    tpos[0] = tbike->pos[0];
    tpos[1] = tbike->pos[1];
    if((ocol = scansafeline(bike->pos, tpos, hpos, this)) == -1)
    {
        ulong   oldflags = ttflags;

        unsetttpos();   // if any set

        AnalogueCtl ana;
        ttpos[0] = tpos[0];
        ttpos[1] = tpos[1];
        ttang    = ana.CalcAngle(tpos[0]-bike->pos[0],tpos[1]-bike->pos[1]);
//      dmsg("%d = CalcAngle(%d %d)", ttang, tpos[0]-bike->pos[0], tpos[1]-bike->pos[1]);
        ttflags  = Whisker::ENEMYDIRECTSIGHT;
        ttdist   = calcdist(bike->pos, tpos);

        //if(!(oldflags & Whisker::ENEMYDIRECTSIGHT))
        //    field.playsound(SND_TERMFOC1);

        return OK;
    }
    else
    if(ttpos[0] && (ttflags & Whisker::ENEMYDIRECTSIGHT))
    {
        ttpos[0] = 0;
        ttflags  = 0;
    }

    // what obstacle did scansafeline run into, at what distance?
    switch(ocol)
    {
        case col_flame:
        case col_fire:
        case col_waveexpl:
        case col_cmimpact:
        case col_mgimpact:
            if(calcdist(bike->pos, hpos) < 50)
            {
                // something dangerous happens in front of us.
                // turn around:
                ttang    = (bike->ana.angle + (ftrig.degmax() >> 1)) & ftrig.degmask();
                ttpos[0] = bike->pos[0] + convif(ftrig.sin(ttang) * 100);
                ttpos[1] = bike->pos[1] - convif(ftrig.cos(ttang) * 100);
                ttflags  = 0;
                ttdist   = 100;
                if(field.inbord(ttpos))
                {
                    action      = RETREAT;
                    actioncnt   = 800;  // for 800 msec
                    return  OK;
                }
                ttpos[0] = 0;   // failed, reset ttpos
            }
    }

    for(ushort i=0; i<WHISKERS; i++)
    {
        wcur = &wisk[i];

        // undraw vdebug line(s), if visible
        if(wcur->vdpos[0][0])
        {
            xorline(wcur->vdpos[0]);
            CLEAR(wcur->vdpos[0]);
        }
        if(wcur->vdpos[1][0])
        {
            xorline(wcur->vdpos[1]);
            CLEAR(wcur->vdpos[1]);
        }

        if(  !(wcur->flags & Whisker::ENEMYSIGHTED)
           && (wcur->delay -= cyclesize) < 0)
        {
            wcur->delay = 100;

            // set new partially-random position for whisker
            // and perform re-scan from it
            long baseang = (long)i * ftrig.degmax() / WHISKERS;
            long varyang = rand() % (ftrig.degmax() / WHISKERS);

            long maxrad, baserad, varyrad;

            if(i < NDWHISK)
            {
                // near distance scan
                maxrad  = 80;
                baserad = maxrad / 5;
                varyrad = rand() % (maxrad * 4 / 5);
            }
            else
            {
                // long distance scan
                maxrad  = field.sizemax / 2;
                baserad = maxrad / 10;
                varyrad = rand() % (maxrad * 9 / 10);
            }

            long wang    = baseang + varyang;
            long wrad    = baserad + varyrad;

            wcur->pos[0] = bike->pos[0] + convif(ftrig.sin(wang) * wrad);
            wcur->pos[1] = bike->pos[1] - convif(ftrig.cos(wang) * wrad);
            wcur->angle  = wang;
            wcur->mdist  = wrad; // remember distance to mother

            // SCAN:
            wcur->flags = 0;

            if(!field.inbord(wcur->pos))
            {
                wcur->angle = 0;
                wcur->mdist = 0;
                continue;
            }

            // 1. from whisker to mothership
            tpos[0] = bike->pos[0];
            tpos[1] = bike->pos[1];
            if(wcur->scan(tpos, col_shape0 + bike->number))
            {
                wcur->flags |= Whisker::MOTHERSIGHTED;
                nmothersighted++;

                wcur->vdpos[0][0]   = wcur->pos[0];
                wcur->vdpos[0][1]   = wcur->pos[1];
                wcur->vdpos[0][2]   = tpos[0];
                wcur->vdpos[0][3]   = tpos[1];
                xorline(wcur->vdpos[0]);

                // 2. if mother sighted, look also for enemy
                tpos[0] = tbike->pos[0];
                tpos[1] = tbike->pos[1];
                if(wcur->scan(tpos, col_shape0 + tbike->number))
                {
                    wcur->flags |= Whisker::ENEMYSIGHTED;
                    // this automatically locks the whisker
                    // until it's reached by mother or unlocked otherwise
                    nenemysighted++;

                    wcur->vdpos[1][0]   = wcur->pos[0];
                    wcur->vdpos[1][1]   = wcur->pos[1];
                    wcur->vdpos[1][2]   = tpos[0];
                    wcur->vdpos[1][3]   = tpos[1];
                    xorline(wcur->vdpos[1]);

                    // calc distance of whisker to enemy:
                    long d[3];
                    d[0] = tbike->pos[0] - wcur->pos[0];
                    d[1] = tbike->pos[1] - wcur->pos[1];
                    wcur->edist = sqrt((ulong)(d[0]*d[0]) + (ulong)(d[1]*d[1]));
                }
            }
        }
    }

    // select next ttpos, if not at least a MOTHERSIGHTED pos'n is set
    bool selnewpos = 0;
    if(!ttpos[0])   selnewpos = 1;
    else
    if(!(ttflags & Whisker::ENEMYSIGHTED) && nenemysighted)
        selnewpos   = 1;    // enemy in sight has more prio than mothersighted

    if(selnewpos)
    {
       short isel = -1;
       ulong fsel = 0;

       if(nenemysighted)
       {
           // scan for mindist
           isel = rand() % nenemysighted;
           fsel = Whisker::ENEMYSIGHTED;

           long    mindist = 100000;
           ushort imindist = WHISKERS;

           for(i=0; i<WHISKERS; i++)
           {
               wcur = &wisk[i];

               if(wcur->flags & fsel)
               {
                   if(wcur->mdist < mindist)
                   {
                       mindist  = wcur->mdist;
                       imindist = i;
                   }
               }
           }

           if(imindist >= WHISKERS)
           {   SysErr(912951333); return INTERNAL;    }

           unsetttpos();    // if any set

           wcur = &wisk[imindist];

           ttpos[0] = wcur->pos[0];
           ttpos[1] = wcur->pos[1];
           AnalogueCtl ana;
           ttang    = ana.CalcAngle(ttpos[0]-bike->pos[0],ttpos[1]-bike->pos[1]);
           ttflags  = fsel;
           ttwindex = imindist;
       }
       else
       if(nmothersighted)
       {
           // scan for maxdist
           isel = rand() % nmothersighted;
           fsel = Whisker::MOTHERSIGHTED;

           long    maxdist = -1;
           ushort imaxdist = WHISKERS;

           for(i=0; i<WHISKERS; i++)
           {
               wcur = &wisk[i];

               if(wcur->flags & fsel)
               {
                   if(wcur->mdist > maxdist)
                   {
                       maxdist  = wcur->mdist;
                       imaxdist = i;
                   }
               }
           }

           if(imaxdist >= WHISKERS)
           {   SysErr(912951334); return INTERNAL;    }

           unsetttpos();    // if any set

           wcur = &wisk[imaxdist];

           ttpos[0] = wcur->pos[0];
           ttpos[1] = wcur->pos[1];
           AnalogueCtl ana;
           ttang    = ana.CalcAngle(ttpos[0]-bike->pos[0],ttpos[1]-bike->pos[1]);
           ttflags  = fsel;
           ttwindex = imaxdist;
       }

       if(isel < 0)
       {
           unsetttpos();    // if any set

           // nothing in sight, drive thru walls to reach enemy.
           ttpos[0] = tbike->pos[0];
           ttpos[1] = tbike->pos[1];
           AnalogueCtl ana;
           ttang    = ana.CalcAngle(ttpos[0]-bike->pos[0],ttpos[1]-bike->pos[1]);
//         dmsg("%d = CalcAngle(%d %d)", ttang, ttpos[0]-bike->pos[0], ttpos[1]-bike->pos[1]);
           ttflags  = 0; // least-important type of ttpos
       }
    }

    return OK;
}
