/*   _____________________________________________________________
    /                                                             \
    | File: explosion.cpp - explosion classes code
    \_____________________________________________________________/

    contents:

        code for

            BaseExpl
            Explosion
            SmallExpl
            BigExpl
            ImpactExpl
            Flame
            OilFlush
            PExpl

    Developers: ID: Name:
    =========== --- -----
                JTH Juergen Thumm
  _____________________________________/VERSION HISTORY\____________________
 /Date_ Version Who What____________________________________________________\
 yymmdd ------- --- ---------------------------------------------------------
 940318 1.26    JTH created, moved 'Explosion' & 'Flame' code into this file;
                    redesign of all explosion classes
 940321 1.30        BaseExpl: tiny pix plot/del 'didn't reach border' fixed
 940417 1.36        BaseExpl: vectors out of bounds die now
 941030             BaseExpl: if in flame mode it now creates also FirePix'
 950416             BaseExpl: keepwalls flag, zero if col_waveexpl.
 950417             BaseExpl: crtfire now also with waveexpl.
*/

#   include "global.h"

//  ModInit(explosion_o);

//|| BaseExpl

BaseExpl::BaseExpl(

    cpix    xpos[2],    // position in playfield
    int     xnvecs,     // number of vectors to be used
    int     xsteps,     // number of steps to be performed

    pcol    xcolcode,   // playfield color code
    short   xncolors,   // number of colors in color cycle palette
    uword   *xpalette,  // if not 0, points to color cycle palette

    short   xslices,    // divide speed by this (eats less CPU time)
    int     xmode       // mode flags

    )

    :   SCALEB      (10),

        PlayObj     (BASE_EXPLOSION, 10, (xmode & BE_MODE_CPUPRIO) ? 0 : 30)
{_
    dead    = 0;

    pos[0]  = xpos[0];
    pos[1]  = xpos[1];

    mode    = xmode;

    // alloc vector table:

    nvecs   = xnvecs;

    vecs    = 0;
    vec2    = 0;
    htp     = 0;

    ifn(vecs = new int[nvecs * 4])
    {   MemErr(402941053);  dead=1; return; }

    if(mode & BE_MODE_TEMP)
    {
        // alloc support tables for temporar drawing:

        ifn(vec2 = new int[nvecs * 4])
        {   MemErr(1302941735); dead=1; return; }

        ifn(htp  = new long[nvecs * 2])
        {   MemErr(2102942039); dead=1; return; }

        memset(htp, 0, nvecs * 2 * sizeof(long));
    }

    steps[0]    = xsteps;   // steps for pixel painting
    steps[1]    = xsteps;   // steps for pixel deletion
    steps[2]    = 3;        // wait so many steps before deleting pixels

    colcode = xcolcode;
    ncolors = xncolors;
    palette = xpalette;
    slices  = xslices;

    colidx  = 0;    // reset color cycling
    slice   = 0;    // reset slices counter
    fatpix  = 0;    // by default, draw tiny pixels
}

BaseExpl::~BaseExpl()
{_p ((ulong)this, (ulong)vecs, (ulong)vec2, (ulong)htp);

    if(vecs) delete [] vecs;

    if(mode & BE_MODE_TEMP)
    {
        if(vec2) delete [] vec2;
        if(htp)  delete [] htp;
    }
}

rcode BaseExpl::step(class PlayMsg* msg)
{_
 int    i;
 cpix   ax,ay,htpos[2];
 pcol   pixcol;
 short  ptun;

 // lying xweapon blast-off support:
 cpix   apos[2];
 short  dir_x;
 enum   Dirs dir_e;
 bool   checkblast =    (       colcode >= col_OBST_MIN
                             && colcode <= col_OBST_MAX )
                    ||          colcode == col_oil;

 bool   crtfire     = ((colcode == col_flame) || (colcode == col_waveexpl));
 bool   oil         =  (colcode == col_oil     );
 bool   keepwalls   =  (colcode != col_waveexpl);

#ifndef RDEMO
 OilDriver *odrv;
#endif

 // performance enhancements:
 int    *vp, vpstep;    // vector pointer'n step
 long   *tvp;           // tunnel vector pointer
 ushort *usp;

    if(dead)    {delete this; return OK;}

    if(msg)     return INDATA;

#ifndef RDEMO
    if(oil && !(odrv = (OilDriver*)field.getobj(OIL_DRIVER)))
        return  FAILED;
#endif

    if(steps[0])
    {
     // draw & animate vectors:

     field.color(colcode);

     vp     = vecs + 4 * slice; // points now to vec(0+slice).posn.x
     vpstep = 4 * slices;       // per i (see below), step pointer so far

     for(i  = slice; i < nvecs; i += slices, vp += vpstep)
     {
        if(*vp == -1)   continue;   // vector is dead

        ax  = *vp     >> SCALEB;
        ay  = *(vp+1) >> SCALEB;

        // === coordinate validity check and tunnel mapping ===

        if(     ax < field.bord[0] || ax >= field.bord[2]
            ||  ay < field.bord[1] || ay >= field.bord[3]   )
        {
         // coords out of bounds. flying through a tunnel?

         if((ptun = field.tunmapxy(ax,ay))!=0)  // if vector passes a tunnel
         {
            *vp     = (int)ax << SCALEB;    // store adjusted xy
            *(vp+1) = (int)ay << SCALEB;

            if(mode & BE_MODE_TEMP) // if delete pass will follow
            {
                // NOTE: htp is available only in BE_MODE_TEMP !!!

                tvp = htp + i * 2;  // calc tunnel vector pointer pos'n
                usp = (ushort*)tvp; // to select upper/lower half

                if(ptun & field.PASSED_HTUNNEL)
                {
                    // passed htunnel.
                    // remember htunnel pos'n for pixel delete pass;
                    // this is updated every time htunnel is passed again.

                    *(usp+1)    = field.htun[0];
                    *(usp+3)    = field.htun[1];
                }

                *(usp+0)    |=  ptun;   // put tunmap result in upper word
            }
         }

         // check coords again if they're now valid.
         // if not, vector is done for.

         if(    ax < field.bord[0] || ax >= field.bord[2]
            ||  ay < field.bord[1] || ay >= field.bord[3]   )
         {
            *vp = -1;   // vector will be ignored from now on
            continue;   // nothing more to do on this vector
         }
        }   // endif out of bounds

        // NOW, ax / ay are VALID COORDS and need no further checks!

        if(checkblast)
        {
          // check if explosion pixel reached something of interest,
          // i.e. lying XWeapon or fixwall

#ifdef  AMIGA
          pixcol = FReadPix((short)ax,(short)ay);
#else
          pixcol = field.readpix(ax,ay);
#endif
          switch(pixcol)
          {
            case col_xweapon:

                if(!oil)
                {
                    // lying XWeapon: blast it off!

                    apos[0] = ax; apos[1] = ay;

                    // convert speed into direction

                    dir_x   = field.convdir((short)(*(vp+2) >> SCALEB),
                                            (short)(*(vp+3) >> SCALEB));

                    dir_e   = field.convdir(dir_x); // conv to enum direction

                    // blast away xweapon in pixel's flying direction

                    field.BlastXWeapon(apos, dir_e);
                }

                break;

            case col_fixwall:

                if(keepwalls)
                {
                    // fix walls can NOT be damaged by explosions.
                    // pixel must stop when reaching one.

                    *vp = -1;   // vector will be ignored from now on
                    continue;   // nothing more to do on this vector
                }

                break;

#ifndef RDEMO

            case col_nitro:

                if(!oil)
                {
                    apos[0] = ax; apos[1] = ay;

                    CrtNitroExpl(apos);
                }

                break;

            default:

                if(crtfire && steps[0] == 1)
                {
                    // this is the last step of this explosion vector.
                    // is there a pixel in further direction which is burnable?

                    apos[0] = (*(vp+0) + *(vp+2)) >> SCALEB;
                    apos[1] = (*(vp+1) + *(vp+3)) >> SCALEB;

                    if(field.inbord(apos))
                    {
                        pcol fcol = field.readpix(apos[0], apos[1]);

                        switch(fcol)
                        {
                            case col_dust:
                            case col_waveexpl:
                            case col_oil:
                            case col_fixwall:
                                // yes: try to set it on fire
                                FireDriver *fdrv = (FireDriver*)
                                                    field.getobj(FIRE_DRIVER);
                                if(fdrv)    fdrv->CreatePix(apos[0], apos[1]);
                                break;
                        }
                    }

                }   // endif crtfire etc.

                break;

#endif      // !defined (RDEMO)

          } // endswitch pixcol

        }   // endif checkblast

        // plot a BaseExpl pixel:

        if(oil)
        {
#ifndef RDEMO
            ifn(steps[0] & 3)   // just every 4th step
                odrv->CreateDrop(ax,ay);
#endif
        }
        else
        if(fatpix)
        {
            // plot a fat (3 x 3) pixel:

            // MUST perform a special coordinate check for this.

            if(     ax > field.bord[0] && ax < field.bord[2]-1
                &&  ay > field.bord[1] && ay < field.bord[3]-1  )
            {
#ifdef  AMIGA
                FWriteFatPix(ax-1, ay-1, colcode);
                FWriteFatPix(ax  , ay  , colcode);
#else
                field.plot(ax  , ay-1);
                field.plot(ax+1, ay  );
                field.plot(ax  , ay+1);
                field.plot(ax-1, ay  );

                field.plot(ax, ay);
#endif
            }
        }
        else
        {
            // plot a tiny pixel: coords were checked above

#ifdef  AMIGA
                FWritePix((short)ax, (short)ay, (short)colcode);
#else
                field.plot(ax, ay);
#endif
        }

        // add speed vectors to pos'n vectors

        *vp     += *(vp+2); // vec.pos.x += vec.speed.x
        *(vp+1) += *(vp+3); // vec.pos.y += vec.speed.y

     }  // endfor i

    }   // endif steps[0]

    // so much for drawing the pixels.
    // now comes the pixel deletion,
    // IF explosion is in temporar mode

    if(mode & BE_MODE_TEMP)
    {

        if(!vec2 || !htp)
            {   SysErr(1803941532); delete this; return INTERNAL;   }

    ifn(steps[2])
    {
     // delete vectors:

     field.color(col_backgrnd);

     vp     = vec2 + 4 * slice; // points now to vec(0+slice).posn.x
     vpstep = 4 * slices;       // per i (see below), step pointer so far

     for(i  = slice; i < nvecs; i += slices, vp += vpstep)
     {
        if(*vp == -1)   continue;   // vector is dead

        ax  = *vp     >> SCALEB;
        ay  = *(vp+1) >> SCALEB;

        tvp = htp + i * 2;  // calc tunnel vector pointer pos'n
        usp = (ushort*)tvp; // to select upper/lower half

        // has pixel flown through a tunnel during pass 1 ?
        if(*(usp+0))        // if any flag set
        {
            // yes: check if this would now be the case again,
            //      if htunnel would have the old position:

            if(*(usp+0) & field.PASSED_HTUNNEL)
            {
                // take stored htunnel pos'n:
                htpos[0]    = *(usp+1);
                htpos[1]    = *(usp+3);
            }
            else
            {
                // take current htunnel pos'n as dummy:
                htpos[0]    = field.htun[0];
                htpos[1]    = field.htun[1];
            }

            if(field.tunmapxy(ax, ay, htpos))
            {
                // yea. so store adapted pos'n:

                *vp     = (int)ax << SCALEB;    // store adjusted xy
                *(vp+1) = (int)ay << SCALEB;
            }
        }

        if(     ax < field.bord[0] || ax >= field.bord[2]
            ||  ay < field.bord[1] || ay >= field.bord[3]   )
        {
            *vp = -1;   // vector will be ignored from now on
            continue;   // nothing more to do on this vector
        }

        // NOW, ax / ay are VALID COORDS and need no further checks!

        // delete a BaseExpl pixel:

        if(fatpix)
        {
            // delete a fat (3 x 3) pixel:

            // MUST perform a special coordinate check for this.

            if(     ax > field.bord[0] && ax < field.bord[2]-1
                &&  ay > field.bord[1] && ay < field.bord[3]-1  )
            {
#ifdef  AMIGA
                FWriteFatPix(ax-1, ay-1, col_backgrnd);
                FWriteFatPix(ax  , ay  , col_backgrnd);
#else
                field.plot(ax  , ay-1);
                field.plot(ax+1, ay  );
                field.plot(ax  , ay+1);
                field.plot(ax-1, ay  );

                field.plot(ax, ay);
#endif
            }
        }
        else
        {
            // delete a tiny pixel: coords were checked above

            if(     field.readpix(ax,ay) == col_fixwall
                &&  keepwalls   )
            {
                // on painting, vector stopped at fix wall.
                // delete vector must do the same.

                *vp = -1;   // vector will be ignored from now on
                continue;   // nothing more to do on this vector
            }

#ifdef  AMIGA
            FWritePix((short)ax, (short)ay, (short)col_backgrnd);
#else
            field.plot(ax, ay);
#endif
        }

        // add speed vectors to pos'n vectors

        *vp     += *(vp+2); // vec.pos.x += vec.speed.x
        *(vp+1) += *(vp+3); // vec.pos.y += vec.speed.y

     }  // endfor i

    }   // endif !steps[2]

    }   // endif BE_MODE_TEMP

    // count CPU time slices

    if(++slice >= slices)
    {
        slice = 0;  // one step performed on all vectors

        if(ncolors > 1)
        {
            ifn(palette)
                {   SysErr(1803941536); delete this; return INTERNAL;   }

            field.loadcol(colcode, palette[colidx++]);

            if(colidx >= ncolors)   colidx = 0; // re-cycle
        }

        if(steps[0])
            steps[0]--; // count down drawing

        if(mode & BE_MODE_TEMP)
        {
            if(steps[2])
                steps[2]--; // wait for deletion start
            else
                steps[1]--; // count down deletion

            // if all steps performed, leave playfield:

            ifn(steps[2])           // if deleting and
                ifn(steps[1])       // done with it,
                {
                    delete  this;   // leave playfield
                    return  OK;
                }
        }
        else
        {
            ifn(steps[0])           // if completed painting,
            {
                    delete  this;   // leave playfield
                    return  OK;
            }
        }
    }

    return OK;
}


//|| Explosion

Explosion::Explosion(

    cpix    xpos[2],
    int     xnvecs,
    int     xsteps,

    pcol    xcolcode,
    short   xncolors,
    uword   *xpalette,

    short   xslices,
    int     mode

    )
    : BaseExpl( xpos, xnvecs, xsteps,
                xcolcode, xncolors, xpalette,
                xslices, mode
              )
{_
 int i,w1,w2;
 int *vp;       // vector pointer
 short  d = 1;  // delta

    if(dead)    return;

    // draw explosion center

    if(colcode == col_waveexpl)
        d = 5;  // plot larger blank center

    if(     colcode != col_mgimpact
        &&  colcode != col_cmimpact
        &&  pos[0] > field.bord[0]+d
        &&  pos[0] < field.bord[2]-1-d
        &&  pos[1] > field.bord[1]+d
        &&  pos[1] < field.bord[3]-1-d

        &&  field.readpix(pos[0]  ,pos[1]  ) != col_fixwall
      )
        field.rectfill(pos[0]-d,pos[1]-d, pos[0]+d,pos[1]+d,
            (mode & BE_MODE_TEMP) ? col_backgrnd : colcode);

    // pixel coord's & speed init

    for(i=0, vp=vecs; i<nvecs; i++) // vp increment see below
    {
        // random coords, they are speeds as well:
        do
        {   w1 = random(10 << SCALEB) - (5 << SCALEB);
            w2 = random(10 << SCALEB) - (5 << SCALEB);
        }
        while(!w1 && !w2);

        *vp++   = w1 + ((int)pos[0] << SCALEB); // scaled x pos'n
        *vp++   = w2 + ((int)pos[1] << SCALEB); // scaled y pos'n
        *vp++   = w1;   // scaled x speed
        *vp++   = w2;   // scaled y speed
    }

    if(mode & BE_MODE_TEMP)
    {
        ifn(vec2)   {   SysErr(1803941601); dead=1; return; }

        memcpy(vec2, vecs, xnvecs * 4 * sizeof(int));
    }
}

//|| SmallExpl

uword SmallExpl::palette[SMALLEXPL_NCOLORS] = {

    0x998,
    0xBB7,
    0XDD6,
    0xFF5,
    0xFF4,
    0XAF3,
    0x8F2,
    0x4F0,
    0x0a0,
    0x040

    };

SmallExpl::SmallExpl(cpix pos[2])

    : Explosion(pos, 16, SMALLEXPL_NCOLORS, col_smallexpl,
        SMALLEXPL_NCOLORS, palette, 5, BE_MODE_TEMP)

{_
}

//|| BigExplosion

BigExpl::BigExpl(cpix pos[2])

    : Explosion(pos, 500, BIGEXPL_NCOLORS, col_bigexpl,
        BIGEXPL_NCOLORS, palette, 6)

{_
}

uword BigExpl::palette[BIGEXPL_NCOLORS] = {

    0xfff,
    0xddd,
    0xbbb,
    0x999,
    0X777,
    0x555,
    0x333,
    0x411,
    0x530,
    0x560,

    0X590,
    0x7c0,
    0xa90,
    0xd60,
    0xe30,
    0xf10,
    0Xf10,
    0xf20,
    0xf30,
    0xf40,

    0xf50,
    0xf60,
    0Xf70,
    0xf80,
    0xf90,
    0xfa0,
    0xfb0,
    0xfc0,
    0Xfdf,
    0xfee,

    0Xffc,
    0xffa,
    0Xff8,
    0xff6,
    0Xff4,
    0xee2,
    0Xdd0,
    0xcc0,
    0Xbb0,
    0xaa0

    };

//|| ImpactExpl

ImpactExpl::ImpactExpl(cpix pos[2], pcol col,
        int vecs, int steps, short slices, int modes)

    : Explosion(pos, vecs, steps, col, 1, 0, slices, modes)
{_
}

#ifndef RDEMO

//|| Flame

char Flame::dvct[8][2][2] = {

//  xm xd  ym  yd       <-xmain/xdelta ymain/ydelta
    0,  1, -1,  0,  // UP direction: subtract main random value from y
    1,  1, -1,  1,  // UP+RIGHT
    1,  0,  0,  1,  // RIGHT
    1,  1,  1, -1,  // RIGHT+DOWN
    0,  1,  1,  0,  // DOWN
    -1, 1,  1,  1,  // LEFT+DOWN
    -1, 0,  0,  1,  // LEFT: vary x a lot, but y less
    -1, 1, -1, -1,  // LEFT+UP: take main for both and vary both a bit

    };

Flame::Flame(

    cpix    pos[2],
    Bike    *bk,

    int     xnvecs,
    int     xsteps,

    pcol    xcol,
    short   xncols,
    uword   *xpalette,

    short   xslices,

    int     xtramodes

    )

    :   FLAMEDIST   (xtramodes ? 20 : 30), // CPUPRIO flames nearer

        BaseExpl(pos, xnvecs, xsteps,
                 xcol, xncols, xpalette,
                 xslices, xtramodes | BE_MODE_TEMP
                )
{_
 int    w1,w2,mval,delta;
 int    i,xdir;
 cpix   ax,ay;
 int    *vp;        // vector pointer
 long   rsum,rx,ry; // analog direction stuff. if rsum==0, use digi dir

    if(dead)    return;

    if(!htp)    {SysErr(1309941112); return;}

    xdir    = field.convdir(bk->dir);

    if(bk->ana.log)
    {
        rx      = (long)bk->ana.vdir[0];
        ry      = (long)bk->ana.vdir[1];
        rsum    = ABS(rx) + ABS(ry);
    }
    else
        rsum    = 0;    // use digital direction

    // draw initial space in front of bike:

    field.color(col_backgrnd);

    for(i=2; i<FLAMEDIST; i += 2)
    {
        if(rsum)
        {
            ax = (cpix)(pos[0] + rx * i / rsum);
            ay = (cpix)(pos[0] + ry * i / rsum);
        }
        else
        {
            ax = pos[0] + dvct[xdir][0][0] * i;
            ay = pos[1] + dvct[xdir][1][0] * i;
        }

        if(     ax > field.bord[0] && ax < field.bord[2]-1
            &&  ay > field.bord[1] && ay < field.bord[3]-1  )
        {
            field.rectfill(ax-1, ay-1, ax+1, ay+1); // draw 3 x 3 rect
        }
    }

    // flame pixel coord's & speed init:
    // the vectors must fly into one of 8 directions

    for(i=0, vp=vecs; i<nvecs; i++) // vp increment see below
    {
        *(htp + i * 2 + 0)  = -1;   // make htp entry invalid

        // --- setup initial positions in some distance to bike ---

        if(rsum)
        {
            *vp++ = rx * (FLAMEDIST<<SCALEB) / rsum + ((int)pos[0]<<SCALEB);
            *vp++ = ry * (FLAMEDIST<<SCALEB) / rsum + ((int)pos[1]<<SCALEB);
        }
        else
        {
            *vp++ = dvct[xdir][0][0] * (FLAMEDIST<<SCALEB)
                    + ((int)pos[0]<<SCALEB);

            *vp++ = dvct[xdir][1][0] * (FLAMEDIST<<SCALEB)
                    + ((int)pos[1]<<SCALEB);
        }

        // --- setup speed: note that flame 'pixels' have size 3 x 3 ---

        mval = random(2 << SCALEB) + (2 << SCALEB);

        // if driving diagonal, divide speed by sqrt(2):

        // if(dvct[xdir][0][0] && dvct[xdir][1][0])
        //  mval = mval / 1.4142;

        // use main random value for x/y speed init:

        if(rsum)
        {
            w1  = rx * mval / rsum;
            w2  = ry * mval / rsum;
        }
        else
        {
            w1  = dvct[xdir][0][0] * mval;
            w2  = dvct[xdir][1][0] * mval;
        }

        // vary x/y speed by a delta value:
        // in this case, just the digital direction part is used
        // for easier calculations

        delta   = ((1<<SCALEB) * i / nvecs) - (1<<SCALEB) / 2;

        *vp++   = w1 + dvct[xdir][0][1] * delta;
        *vp++   = w2 + dvct[xdir][1][1] * delta;
    }

    // copy 'draw' vector table to 'delete' vector table:

    ifn(vec2)   {   SysErr(1803941601); dead=1; return; }

    memcpy(vec2, vecs, xnvecs * 4 * sizeof(int));

    fatpix  = 1;    // draw fat pixels
}

//|| OilFlush

char OilFlush::dvct[8][2][2] = {

//  xm xd  ym  yd       <-xmain/xdelta ymain/ydelta
    0,  1, -1,  0,  // UP direction: subtract main random value from y
    1,  1, -1,  1,  // UP+RIGHT
    1,  0,  0,  1,  // RIGHT
    1,  1,  1, -1,  // RIGHT+DOWN
    0,  1,  1,  0,  // DOWN
    -1, 1,  1,  1,  // LEFT+DOWN
    -1, 0,  0,  1,  // LEFT: vary x a lot, but y less
    -1, 1, -1, -1,  // LEFT+UP: take main for both and vary both a bit

    };

OilFlush::OilFlush(

    cpix    pos[2],
    Bike    *bk,

    int     xnvecs,
    int     xsteps,

    pcol    xcol,
    short   xncols,
    uword   *xpalette,

    short   xslices,

    int     xtramodes

    )

    :   OFLUSHDIST  (xtramodes ? 20 : 30), // CPUPRIO flushes nearer

        BaseExpl(pos, xnvecs, xsteps,
                 xcol, xncols, xpalette,
                 xslices, xtramodes
                )
{_
 int    w1,w2,mval,delta;
 int    i,xdir;
 cpix   ax,ay;
 int    *vp;        // vector pointer
 long   rsum,rx,ry; // analog direction stuff. if rsum==0, use digi dir

    if(dead)    return;

    xdir    = field.convdir(bk->dir);

    if(bk->ana.log)
    {
        rx      = (long)bk->ana.vdir[0];
        ry      = (long)bk->ana.vdir[1];
        rsum    = ABS(rx) + ABS(ry);
    }
    else
        rsum    = 0;    // use digital direction

    // draw initial space in front of bike:

    field.color(col_backgrnd);

    for(i=2; i<OFLUSHDIST; i += 2)
    {
        if(rsum)
        {
            ax = (cpix)(pos[0] + rx * i / rsum);
            ay = (cpix)(pos[0] + ry * i / rsum);
        }
        else
        {
            ax = pos[0] + dvct[xdir][0][0] * i;
            ay = pos[1] + dvct[xdir][1][0] * i;
        }

        if(     ax > field.bord[0] && ax < field.bord[2]-1
            &&  ay > field.bord[1] && ay < field.bord[3]-1  )
        {
            field.rectfill(ax-1, ay-1, ax+1, ay+1); // draw 3 x 3 rect
        }
    }

    // flush pixel coord's & speed init:

    for(i=0, vp=vecs; i<nvecs; i++) // vp increment see below
    {
        // --- setup initial positions in some distance to bike ---

        if(rsum)
        {
            *vp++ = rx * (OFLUSHDIST<<SCALEB) / rsum + ((int)pos[0]<<SCALEB);
            *vp++ = ry * (OFLUSHDIST<<SCALEB) / rsum + ((int)pos[1]<<SCALEB);
        }
        else
        {
            *vp++ = dvct[xdir][0][0] * (OFLUSHDIST<<SCALEB)
                    + ((int)pos[0]<<SCALEB);

            *vp++ = dvct[xdir][1][0] * (OFLUSHDIST<<SCALEB)
                    + ((int)pos[1]<<SCALEB);
        }

        // --- setup speed ---

        mval = random(1 << (SCALEB - 1)) + (1 << SCALEB);

        // use main random value for x/y speed init:

        if(rsum)
        {
            w1  = rx * mval / rsum;
            w2  = ry * mval / rsum;
        }
        else
        {
            w1  = dvct[xdir][0][0] * mval;
            w2  = dvct[xdir][1][0] * mval;
        }

        // vary x/y speed by a delta value:
        // in this case, just the digital direction part is used
        // for easier calculations

        delta   = ((2<<SCALEB) * i / nvecs) - (2<<SCALEB) / 2;

        *vp++   = w1 + dvct[xdir][0][1] * delta;
        *vp++   = w2 + dvct[xdir][1][1] * delta;
    }
}

//|| PExpl  - pressure explosion

uword PExpl::palette[PEXPL_NCOLORS] = {

    0x630,
    0x740,
    0x850,
    0x960,
    0Xa70,
    0xb80,
    0xc90,
    0Xda0,
    0xeb0,
    0xfc0,
    0xed0,
    0xde0,
    0xcf0,
    0xbd0,
    0xab0,
    0x990,
    0x870,
    0x750

    };

PExpl::PExpl(cpix pos[2], int charge)

    :   Explosion
        (
            pos,
            (long)(80 + charge * 20) * 75L / 100L,  // vecs
            PEXPL_NCOLORS,  // steps
            col_pexpl,
            PEXPL_NCOLORS, palette,
            2, BE_MODE_TEMP     // slices, mode
        )
{_
    if(!charge) {SysErr(1011941835); return;}

    // now emulate a pressure wave hitting bikes that
    // are in vincinity

    Bike    *bike,*hibk=0;
    long    d[3],pperc,maxhoov,maxspeed;
    intf    newspeed;
    pcol    colbnd[2]={col_OBST_MIN+1,col_OBST_MAX-3};
    cpix    wallpos[2];

    //  maximum impact radius
    long    rad = field.sizemax / 6;

    if(!rad)    {SysErr(1411941341); return;}

    for(ushort i=0; i<USHRT_MAX; i++)
    {
        if(i < 2)
            bike = (Bike*)field.getobj(i ? BIKE1 : BIKE0);
        else
        {
            if(hibk)
                hibk    = (Bike*)field.nextobj(hibk, HI_BIKE);
            else
                hibk    = (Bike*)field.getobj(HI_BIKE);

            ifn(bike = hibk)    break;  // no further bikes
        }

        if(!bike)

            continue;

        // bike in vincinity or is fixwall between us?

        if(field.scanline(pos, bike->pos, colbnd, wallpos) != -1)

            continue;   // wall: no pressure impact

        // calc position delta and distance of this to bike

        d[0]    = bike->pos[0] - pos[0];
        d[1]    = bike->pos[1] - pos[1];
        d[2]    = sqrt((ulong)(d[0]*d[0]) + (ulong)(d[1]*d[1]));

        // calc pressure impact index on bike
        // 0% if distance is 'rad', 100% in explosion centre

        if((pperc = 100 - d[2] * 100 / rad) > 0)    // if any impact
        {
            // let bike hoover

            bike->hoovering = 1;

            // set bike's direction as direction of pressure (if any)

            if(d[0] || d[1])
            {
                // analogue part

                bike->ana.vdir[0]   = (short)d[0];
                bike->ana.vdir[1]   = (short)d[1];

                // digital part: 1st calc index-dir,
                //               then enum dir from that

                bike->dir   = field.convdir(field.convdir(d[0], d[1]));
            }

            // set bike's speed

            maxspeed    = (long)(80 + charge * 20) * 300L / 100L;
            newspeed    = convfi(maxspeed * pperc / 100);

            if(newspeed > bike->speed)  bike->speed = newspeed;

            // let a main bike scream if impact is > 80%

            if(bike->number < 2 && pperc > 80)

                field.playsound(SND_PGUNHXS, 1);
        }
    }

    //  Blast nearby missiles

    for(    Missile* missile = (Missile*)field.getobj(MISSILE);
            missile;
            missile = (Missile*)field.nextobj(missile, MISSILE)
       )
    {
        d[0]    = missile->pos[0] - pos[0];
        d[1]    = missile->pos[1] - pos[1];
        d[2]    = sqrt((ulong)(d[0]*d[0]) + (ulong)(d[1]*d[1]));

        if((pperc = 100 - d[2] * 100 / rad) >= 30)  // if >= 30 %
            missile->blast();   // next step() of missile blasts it
    }

    //  NEW 220495: blast nearby rockets

    for(    Rocket* rocket = (Rocket*)field.getobj(ROCKET);
            rocket;
            rocket = (Rocket*)field.nextobj(rocket, ROCKET)
       )
    {
        // rockets are active. blast those hit by pressure wave.

        d[0]    = rocket->pos[0] - pos[0];
        d[1]    = rocket->pos[1] - pos[1];
        d[2]    = sqrt((ulong)(d[0]*d[0]) + (ulong)(d[1]*d[1]));

        if((pperc = 100 - d[2] * 100 / rad) >= 30)  // if >= 30 %
            rocket->blast();    // next step() of rocket blasts it
    }

    // blast off all xweapons reached by pressure wave

    long maxdelay;

    XWeaponAnchor *anc = (XWeaponAnchor*)field.getobj(XWEAPON_ANCHOR);

    while(anc)
    {
        d[0]    = anc->pos[0] - pos[0];
        d[1]    = anc->pos[1] - pos[1];
        d[2]    = sqrt((ulong)(d[0]*d[0]) + (ulong)(d[1]*d[1]));

        if(d[2] < rad)
        {
            // calc inverted pressure impact percentage

            pperc = d[2] * 100 / rad;

            // maximum explosion delay at rim of explosion:

            maxdelay = (long)(80 + charge * 20);    // * 100 / 100L;

            // (any) xweapon in sight in that direction?

            if( field.scanline(pos, anc->pos, colbnd, wallpos)

                == col_xweapon )
            {
                // yes: blast off!

                field.BlastXWeapon(

                        wallpos,

                        field.convdir(field.convdir(d[0], d[1])),

                        // delay: 10 msec in centre, maxdelay at rim
                        // (pperc is now MAX at rim, see calc. above!)

                        max(maxdelay * pperc / 100, 10)

                        );
            }
        }

        anc = (XWeaponAnchor*)field.nextobj(anc, XWEAPON_ANCHOR);
    }
}

//|| NitroExpl

void CrtNitroExpl(cpix pos[2])
{_
    cpix x1 = pos[0]-13, y1 = pos[1]-13;
    cpix x2 = pos[0]+13, y2 = pos[1]+13;

    if(x1  < field.bord[0]) x1 = field.bord[0];
    if(x2 >= field.bord[2]) x2 = field.bord[2] - 1;
    if(y1  < field.bord[1]) y1 = field.bord[1];
    if(y2 >= field.bord[3]) y2 = field.bord[3] - 1;

    field.rectfill(x1,y1, x2,y2, col_backgrnd);

    new PExpl(pos);

    field.playsound(SND_NITROXPL);
}

#endif  // !defined(RDEMO)
