
////////////////////////////////////////////////////////////////////////////////   INCLUDES
#include <stdio.h>
#include <allegro.h>
#include <winalleg.h>
#include <windows.h>
#include <math.h>


////////////////////////////////////////////////////////////////////////////////   DEFINES

#define DEBUGMODE 0

#define ZOOM 1
#define BASEW 400
#define BASEH 400
#define OFFSETX 3
#define OFFSETY 25
#define REVOLVESPEED .01f
#define REVOLVEINTERLEAVE 2
#define TRIDIUS 200
#define FPS 60
#define MAXSHOTS 512
#define PLAYERSPEED 8.2f
#define PLAYERACC .8f
#define SHOTSPEED 4.2
#define NORMAL .0625 // 1/16
#define VIEWPORTS 4
#define MAXCLOUDS 8
#define CLOUD_SPRITES 2
#define MAXSHIPS 2
#define TRAIL 80
#define ENOMY 32
#define NMETYPES 2
#define NMEBOUNCE 12.2
#define NMEBOUNCEGAIN 1.0
#define POINTDIST 20
#define LINEDIST 8
#define RANDLINES 4
#define RANDLINESRAND 16
#define NMEBOUNCETIME 200
#define SHOTBOUNCEGAIN 1.1
#define WINDOWDEC .95
#define WINDOWBOUNCE 3.5
#define CLOUDSPEED 8.0
#define BULLETTIME .33

////////////////////////////////////////////////////////////////////////////////   STRUCTS

typedef struct MRT
{
    BITMAP
    *albedo, // colour
    *glow, // specular glow
    *overlay, // hud
    *render; // combined
} MRT;

typedef struct SPRITE
{
    BITMAP *diffuse // colour
    ,*glow; // spec glow
    int frames; // num of frames [TODO]
    int cx,cy; // centre
    int w,h; // dimensions
} SPRITE;

typedef struct BULLET
{
    int type // [TODO[ ?
    ,x,y, // position
    on, // draw?
    life, // time until vapour
    frame; // current
    float fx,fy // float position
    ,vx,vy // float velocity
    ,a; // angle
    SPRITE *s[2]; // sprite
} BULLET;


typedef struct SHIP
{
    int
    maxhp, // endurance
    rapidfire, //shot rate
    bulletlife, // yaknow
    shotdamage, // yaknowtoo
    price; // how much monies 4 dis one
    float
    acceleration, // what could it be..
    velocity, // terminal
    shotvelocity, // even more terminal
    accuracy; // less terminal
    char name[32]; // a description in at least 8 sentences
    SPRITE *s,*shot[2]; // looks and bullet looks
} SHIP;

typedef struct PLAYER
{
    int x,y // coordinates
    ,cash // spare change
    ,lives // elvis anagram
    ,hp // hit pit
    ,shotdelay; // reload time
    SHIP *ship; // vessel
    float fx,fy,vx,vy; // float pos and vel
} PLAYER;


typedef struct CLOUD
{
    int x,y,v; // pos and vel
    float fx,fy,vx,vy;
    SPRITE *s; // sprite
} CLOUD;

typedef struct WINDOW
{
    RECT rect,screen;
    float x,y,vx,vy;
    int triangle[3][2],w,h;
} WINDOW;


// DO THIS:
// - enter on atan2 from upper halfpipe
// - clockwalk
// - if shot, hit with your ship to diminish hp
// - bouncing after being shot
// - glow buffer electrocute effects
// - trail smoke / fire effect
// - make sprites rotate
// - angular velocity = 1 / (velocity dot reflection)
//
// ...
//
// sound cool enough

typedef struct ENEMY
{
    int x,y, // position
    frame, // current frame
    trail[TRAIL][2], // of tears
    current_trail, // ^ this one ^
    hit, // did i hitit?
    hp, // number of hungarian prostitutes
    ai, // artificial intelligence = time until direction change
    speed, // terminal
    enter, // should be return
    dead; // or alive 3
    float fx,fy,vx,vy,v,a,av; // pos, vel, speed, angle, angular vel
    SPRITE *s[2]; // looks
} ENEMY; // no these do not shoot

MRT *mrt;
BULLET shots[MAXSHOTS];
CLOUD clouds[MAXCLOUDS];
int numshots;
BITMAP *buffer;
SPRITE player_shot;
volatile int tim;
HRGN region;
POINT rgnpts[3];
float revolve,tri[3][2],nor[3][2];
PLAYER *player;
SHIP *ships[MAXSHIPS];
ENEMY *nme[ENOMY],*nmetypes[NMETYPES];
int fps=0,tempfps=0,frames=0;
int current_viewport=3;
BITMAP *viewport[VIEWPORTS];
int r=0,g=0,b=0;
float fr=0,fg=0,fb=0;
int gamespeed=30;
SAMPLE *jingle,
    *ship_ricochet,
    *nme_ricochet,
    *shoot_sound,
    *kaboom;
int revolve_interleave=0;
WINDOW window;
int bullettime=0;



void timer()
{
    tim++;
}
END_OF_FUNCTION(timer);



////////////////////////////////////////////////////////////////////////////////   MATH

void normalise(float *x,float *y)
{
    float d;
    d=sqrt((*x)*(*x)+(*y)*(*y));
    (*x)/=d;
    (*y)/=d;
}

void normalise3(float *x,float *y,float *z)
{
    float d;
    d=sqrt((*x)*(*x)+(*y)*(*y)+(*z)*(*z));
    (*x)/=d;
    (*y)/=d;
    (*z)/=d;
}

void normalise3v(float *v)
{
    float d;
    d=sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
    v[0]/=d;
    v[1]/=d;
    v[2]/=d;
}

void cross3v(float *a,float *b,float *c)
{
    c[0]=a[1]*b[2]-a[2]*b[1];
    c[1]=a[2]*b[0]-a[0]*b[2];
    c[2]=a[0]*b[1]-a[1]*b[0];
}

float length(float x,float y)
{
    return sqrt((x)*(x)+(y)*(y));
}

float length3(float x,float y,float z)
{
    return sqrt((x)*(x)+(y)*(y)+(z)*(z));
}

float length3v(float *a)
{
    return sqrt((a[0])*(a[0])+(a[1])*(a[1])+(a[2])*(a[2]));
}


float dot(float x1,float y1,float x2,float y2)
{
    return (x1*x2 + y1*y2);
}

float dot3(float x1,float y1,float z1, float x2,float y2,float z2)
{
    return (x1*x2 + y1*y2 + z1*z2);
}

float dot3v(float *a,float *b)
{
    return (a[0]*b[0] + a[1]*b[1] + a[2]*b[2]);
}

float line_distance(float x,float y,float x1,float y1,float x2,float y2)
{
    float A = x - x1;
    float B = y - y1;
    float C = x2 - x1;
    float D = y2 - y1;

    return abs(A * D - C * B) / sqrt(C * C + D * D);
}

float distance(float x1,float y1,float x2,float y2)
{
    return length(x1-x2,y1-y2);
}

float distance3(float x1,float y1,float z1,float x2,float y2,float z2)
{
    return length3(x1-x2,y1-y2,z1-z2);
}

float distance3v(float *a,float *b)
{
    return length3(a[0]-b[0],a[1]-b[1],a[2]-b[2]);
}

void reflect(float *vx,float *vy,float nx,float ny)
{
    float d=dot(*vx,*vy,nx,ny);
    *vx=*vx-2*d;
    *vy=*vy-2*d;
}



////////////////////////////////////////////////////////////////////////////////   DEFERRED SHADING


SPRITE *load_sprite(const char * filename)
{
    char file[64];
    SPRITE *s;
    int x,y,pxa,pxb,pxc,px;
    char red,green,blue;
    float a[3],b[3],c[3],pa,pb,ha,hb,p;
    s=malloc(sizeof(SPRITE));
    sprintf(file,"img/%s_diffuse.bmp",filename);
    s->diffuse=load_bmp(file,0);
    sprintf(file,"img/%s_glow.bmp",filename);
    s->glow=load_bmp(file,0);
    s->w=s->diffuse->w;
    s->h=s->diffuse->h;
    // if (s->specular->w!=s->w || s->specular->h!=s->h)
    s->cx=s->w/2;
    s->cy=s->h/2;
    return s;
}

void load_sprites(SPRITE **s,const char* filename,int num)
{
    int c;
    char file[64];
    for (c=0;c<num;c++)
    {
        sprintf(file,"%s%i",filename,c);
        printf("Loading %s at %i/%i\n",file,c,num);
        s[c]=load_sprite(file);
    }

}


MRT *create_mrt()
{
    MRT *m;
    m = malloc(sizeof(MRT));
    m->albedo=create_bitmap(BASEW,BASEH);
    m->glow=create_bitmap(BASEW,BASEH);
    m->overlay=create_bitmap(BASEW,BASEH);
    m->render=create_bitmap(BASEW,BASEH);
    return m;

}

void draw_sprite_mrt(MRT *m,SPRITE *s,int xx, int yy)
{
    int x,y;
    x=xx-s->cx;
    y=yy-s->cy;
    draw_sprite(m->glow,s->glow,x,y);
    draw_sprite(m->albedo,s->diffuse,x,y);
}


void clear_mrt(MRT *m)
{
    clear_to_color(m->albedo,makecol(32,160, 96));
    clear_to_color(m->glow,makecol(0,0,0));
    clear_to_color(m->overlay,makecol(0xff,0,0xff));
    clear_to_color(m->render,makecol(0,0,0));
}


void render(MRT *m)
{
    draw_sprite(m->render,m->albedo,0,0);
    set_add_blender(255,255,255,255);
    draw_trans_sprite(m->render,m->glow,0,0);
    set_add_blender(128,128,128,128);
    draw_trans_sprite(m->render,m->glow,-1,0);
    draw_trans_sprite(m->render,m->glow,1,0);
    draw_sprite(buffer,viewport[current_viewport],0,0);
    draw_sprite(buffer,m->overlay,0,0);

}

////////////////////////////////////////////////////////////////////////////////   RENDERING

void draw_clouds()
{
    int c,f;
    for (c=0;c<MAXCLOUDS;c++)
    {
        draw_sprite_mrt(mrt,clouds[c].s,clouds[c].x,clouds[c].y);
        //set_add_blender(255,255,255,255);
       // if (clouds[c].y>0 && clouds[c].y<BASEH) draw_trans_sprite(mrt->glow,clouds[c].s,clouds[c].x,clouds[c].y+=clouds[c].v);
    }
}



void draw_shots()
{
    int c,f;
    for (c=0;c<MAXSHOTS;c++)
    {
        if (shots[c].on)
        {
            //draw_sprite(buffer,shots[c].s[f]->diffuse,shots[c].x-shots[c].s[f]->cx,shots[c].y-shots[c].s[f]->cy);
            draw_sprite_mrt(mrt,shots[c].s[shots[c].frame^=1],shots[c].x,shots[c].y);
        }
    }


}
void draw_nme()
{
    int c,f,i,j,x1,y1,x2,y2,r,g,b;
    for (c=0;c<ENOMY;c++)
    {
        if (!nme[c]->dead)
        {

            //draw_sprite(buffer,shots[c].s[f]->diffuse,shots[c].x-shots[c].s[f]->cx,shots[c].y-shots[c].s[f]->cy);
            nme[c]->frame++;
            draw_sprite_mrt(mrt,nme[c]->s[(nme[c]->frame&4)>>2],nme[c]->x,nme[c]->y);
            if (nme[c]->hit)
            {
                for (
                i=nme[c]->current_trail,
                j=(nme[c]->current_trail==TRAIL-1 ? 0 : nme[c]->current_trail+1);
                j!=nme[c]->current_trail;
                i=(i==TRAIL-1 ? 0 : i+1),
                j=(j==TRAIL-1 ? 0 : j+1)
                )
                {
                    for (f=0;f<RANDLINES;f++)
                    {
                        x1=nme[c]->trail[i][0]+(rand()%RANDLINESRAND)-(RANDLINESRAND/2);
                        y1=nme[c]->trail[i][1]+(rand()%RANDLINESRAND)-(RANDLINESRAND/2);
                        x2=nme[c]->trail[j][0]+(rand()%RANDLINESRAND)-(RANDLINESRAND/2);
                        y2=nme[c]->trail[j][1]+(rand()%RANDLINESRAND)-(RANDLINESRAND/2);
                        r=rand()%255;
                        g=32+rand()%64;
                        b=192+rand()%63;
                        r*=nme[c]->ai;g*=nme[c]->ai;b*=nme[c]->ai;
                        r/=NMEBOUNCETIME;g/=NMEBOUNCETIME;b/=NMEBOUNCETIME;
                        line(mrt->glow,x1,y1,x2,y2,makecol(r,g,b));
                    }
                }
            }
        }
    }


}
void draw_player(PLAYER *p)
{
    draw_sprite_mrt(mrt,p->ship->s,p->x,p->y);
    //draw_sprite(buffer,p->s->diffuse,p->x-p->s->cx,p->y-p->s->cy);

}

void debug_display()
{
    //textprintf_centre_ex(buffer, font, BASEW/2, BASEH/2  , makecol(255,255,0), -1, "FPS: %i",fps);
    //for ( i=0 , j=2; i<3; j=i++) textprintf_centre_ex(buffer, font, BASEW/2, BASEH/2+(i+1)*10  , makecol(255,255,0), -1, "dist%i %1.2f",i,line_distance(player->fx,player->fy,tri[i][0],tri[i][1],tri[j][0],tri[j][1]));
    //   for ( i=0 , j=2; i<3; j=i++) textprintf_centre_ex(buffer, font, BASEW/2, BASEH/2+(i+1)*10  , makecol(255,255,0), -1, "dist%i %1.2f",i,distance(player->fx,player->fy,tri[i][0],tri[i][1]));
    //line(mrt->overlay,BASEW/2,BASEH/2,BASEW/2+nor[0][0]*28,BASEH/2+nor[0][1]*28,makecol(255,255,255));
    //line(mrt->overlay,BASEW/2,BASEH/2,BASEW/2+nor[1][0]*28,BASEH/2+nor[1][1]*28,makecol(255,255,255));
    //line(mrt->overlay,BASEW/2,BASEH/2,BASEW/2+nor[2][0]*28,BASEH/2+nor[2][1]*28,makecol(255,255,255));
    //draw_sprite_debug(buffer,dbg,BASEW/2,BASEH/2);
    line(mrt->overlay,tri[0][0],tri[0][1],tri[1][0],tri[1][1],makecol(255,255,255));
    line(mrt->overlay,tri[2][0],tri[2][1],tri[1][0],tri[1][1],makecol(255,255,255));
    line(mrt->overlay,tri[0][0],tri[0][1],tri[2][0],tri[2][1],makecol(255,255,255));

    textprintf_ex(mrt->overlay, font, 0, 0  , makecol(255,255,0), -1, "FPS: %i",fps);
    if (current_viewport==0) textprintf_ex(mrt->overlay, font, 0, 10  , makecol(255,255,0), -1, "Colourspace viewport");
    if (current_viewport==1) textprintf_ex(mrt->overlay, font, 0, 10  , makecol(255,255,0), -1, "Specular glow viewport");
    if (current_viewport==2) textprintf_ex(mrt->overlay, font, 0, 10  , makecol(255,255,0), -1, "Overlay viewport");
    if (current_viewport==3) textprintf_ex(mrt->overlay, font, 0, 10  , makecol(255,255,0), -1, "Final render");
}

////////////////////////////////////////////////////////////////////////////////   RENDERING BATCH

void rysuj()
{
    int i,j;

    draw_shots();
    draw_nme();
    draw_player(player);
    draw_clouds();
    if (DEBUGMODE) debug_display();

    render(mrt);

    frames++;
    tempfps++;
}


////////////////////////////////////////////////////////////////////////////////   MECHANICS


void update_clouds()
{
    int c,f;
    for (c=0;c<MAXCLOUDS;c++)
    {

        clouds[c].vy=CLOUDSPEED-window.vy;
        clouds[c].vx=-window.vx;
        clouds[c].fx+=clouds[c].vx;
        clouds[c].fy+=clouds[c].vy;
        if (clouds[c].fx>BASEW) clouds[c].fx-=BASEW;
        if (clouds[c].fx<0) clouds[c].fx+=BASEW;
        if (clouds[c].fy>BASEH)
        {
            clouds[c].fy=-1.0*(rand()%500);
            clouds[c].fx=rand()%BASEH;
        }
        if (clouds[c].fy<-500) clouds[c].fy=BASEH;
        clouds[c].x=clouds[c].fx;
        clouds[c].y=clouds[c].fy;


    }
}

void update_player(PLAYER *p)
{
    int i,j,k,kp=0,wh=0;
    float d;

    for ( i=0 , j=2; i<3;i++,j=i-1)
    {
        d=distance(p->fx,p->fy,tri[i][0],tri[i][1]);
        if (d<POINTDIST)
        {
            p->vx=nor[j][0]*p->ship->velocity;
            p->vy=nor[j][1]*p->ship->velocity;
            wh=1;
        }
    }

    for ( i=0 , j=2; i<3; i++,j=i-1)
    {
        d=line_distance(p->fx,p->fy,tri[i][0],tri[i][1],tri[j][0],tri[j][1]);
        if (d<LINEDIST)
        {
            p->vx=nor[j][0]*p->ship->velocity;
            p->vy=nor[j][1]*p->ship->velocity;
            window.vx=-p->vx*WINDOWBOUNCE;
            window.vy=-p->vy*WINDOWBOUNCE;
            wh=1;
        }
    }

    if (!wh)
    {
        if (key[KEY_UP])
        {
            p->vy-=p->ship->acceleration;
            kp=1;
        }
        if (key[KEY_DOWN])
        {
            p->vy+=p->ship->acceleration;
            kp=1;
        }
        if (key[KEY_LEFT])
        {
            p->vx-=p->ship->acceleration;
            kp=1;
        }
        if (key[KEY_RIGHT])
        {
            p->vx+=p->ship->acceleration;
            kp=1;
        }

        if (length(p->vx,p->vy)>p->ship->velocity)
        {
            normalise(&(p->vx),&(p->vy));
            p->vx*=p->ship->velocity;
            p->vy*=p->ship->velocity;
        }
    } else play_sample(ship_ricochet,128,128,1000,0);


    if (!kp && !wh)
    {
        p->vx*=.86;
        p->vy*=.86;

    }
    p->fx+=p->vx;
    p->fy+=p->vy;
    p->x=p->fx;
    p->y=p->fy;
}


void update_shots(PLAYER *p)
{
    int c,i,j;
    float d,v;
    for (c=0;c<MAXSHOTS;c++)
    {
        if (shots[c].on)
        {
            shots[c].fx+=shots[c].vx;
            shots[c].fy+=shots[c].vy;
            shots[c].x=shots[c].fx;
            shots[c].y=shots[c].fy;
            shots[c].life--;
            if (shots[c].life<0) shots[c].on=0;
            v=length(shots[c].vy,shots[c].vx);

            for ( i=0 , j=2; i<3;i++,j=i-1)
            {
                d=distance(shots[c].fx,shots[c].fy,tri[i][0],tri[i][1]);
                if (d<POINTDIST)
                {
                    shots[c].vx=nor[j][0]*v*SHOTBOUNCEGAIN;
                    shots[c].vy=nor[j][1]*v*SHOTBOUNCEGAIN;
                }
            }

            for ( i=0 , j=2; i<3; i++,j=i-1)
            {
                d=line_distance(shots[c].fx,shots[c].fy,tri[i][0],tri[i][1],tri[j][0],tri[j][1]);
                if (d<LINEDIST)
                {
                    shots[c].vx=nor[j][0]*v*SHOTBOUNCEGAIN;
                    shots[c].vy=nor[j][1]*v*SHOTBOUNCEGAIN;
                }
            }

            for (i=0;i<ENOMY;i++)
            {
                if (distance(nme[i]->fx,nme[i]->fy,shots[c].fx,shots[c].fy)<6)
                {
                    shots[c].on=0;
                    nme[i]->vx=shots[c].vx*NMEBOUNCE/v;
                    nme[i]->vy=shots[c].vy*NMEBOUNCE/v;
                    nme[i]->hit=1;
                    nme[i]->ai=NMEBOUNCETIME;
                    play_sample(kaboom,128,128,1000,0);
                     for (j=0;j<TRAIL;j++)
            {
                nme[i]->trail[j][0]=nme[i]->x+((rand()%64)-32);
                nme[i]->trail[j][1]=nme[i]->y+((rand()%64)-32);
            }
                }
            }
        }

    }
}

void shoot(PLAYER *p)
{
    int x,y;
    if (p->shotdelay--<0)
    {
        x=player->x;
        y=player->y-player->ship->s->cy;
        shots[numshots].on=1;
        shots[numshots].x=x;
        shots[numshots].y=y;

        shots[numshots].fx=x;
        shots[numshots].fy=y;

        shots[numshots].vx=(128-(rand()%256))*p->ship->accuracy;
        shots[numshots].vy=-p->ship->shotvelocity;
        shots[numshots].life=p->ship->bulletlife;
        shots[numshots].frame=0;
        shots[numshots].s[0]=p->ship->shot[0];
        shots[numshots].s[1]=p->ship->shot[1];
        numshots++;
        if (numshots>=MAXSHOTS) numshots=0;
        p->shotdelay=p->ship->rapidfire;
        play_sample(shoot_sound,128,128,1000,0);
    }

}

void update_nme()
{
    int c,i,j;
    float a,d,v;
    for (c=0;c<ENOMY;c++)
    {
        if (nme[c]->dead)
        {
            printf("Respawning nme %i\n",c);
            memcpy(nme[c],nmetypes[0],sizeof(ENEMY));
            a = ((rand()%256)/256.0)*M_PI;
            nme[c]->fx=-(cos(a)*TRIDIUS)+(BASEW/2);
            nme[c]->fy=-(sin(a)*TRIDIUS)+(BASEH/2);
            nme[c]->x=nme[c]->fx;
            nme[c]->y=nme[c]->fy;
            nme[c]->vx=cos(a)*nme[c]->v;
            nme[c]->vy=sin(a)*nme[c]->v;
            nme[c]->a=0;
            nme[c]->av=0;
            nme[c]->dead=0;
            nme[c]->enter=1;
            nme[c]->ai=50;
            nme[c]->current_trail=0;
            nme[c]->frame=rand()%16;
            for (i=0;i<TRAIL;i++)
            {
                nme[c]->trail[i][0]=nme[c]->x;
                nme[c]->trail[i][1]=nme[c]->y;
            }
        }
        nme[c]->fx+=nme[c]->vx;
        nme[c]->fy+=nme[c]->vy;
        nme[c]->fx-=window.vx/REVOLVEINTERLEAVE;
        nme[c]->fy-=window.vy/REVOLVEINTERLEAVE;

        nme[c]->ai--;
        if (nme[c]->enter)
        {
            if (nme[c]->ai<0)
            {
                printf("nme %i entered at pos %1.2f %1.2f\n",c,nme[c]->fx,nme[c]->fy);
                nme[c]->ai=nme[c]->speed;
                nme[c]->enter=0;
            }
        }
        else
        {
            if (nme[c]->hit)
            {
                for ( i=0 , j=2; i<3;i++,j=i-1)
                {
                    d=distance(nme[c]->fx,nme[c]->fy,tri[i][0],tri[i][1]);
                    if (d<POINTDIST)
                    {
                        v=length(nme[c]->vx,nme[c]->vy);
                        nme[c]->vx=nor[j][0]*NMEBOUNCEGAIN*v;
                        nme[c]->vy=nor[j][1]*NMEBOUNCEGAIN*v;
                        play_sample(nme_ricochet,128,128,1000,0);
                    }
                }

                for ( i=0 , j=2; i<3; i++,j=i-1)
                {
                    d=line_distance(nme[c]->fx,nme[c]->fy,tri[i][0],tri[i][1],tri[j][0],tri[j][1]);
                    if (d<LINEDIST)
                    {
                        v=length(nme[c]->vx,nme[c]->vy);
                        play_sample(nme_ricochet,128,128,1000,0);
                        nme[c]->vx=nor[j][0]*NMEBOUNCEGAIN*v;
                        nme[c]->vy=nor[j][1]*NMEBOUNCEGAIN*v;
                    }
                }
                if (nme[c]->ai<0)
                {
                    nme[c]->hit=0;
                }
            }
            else
            {
                if (nme[c]->ai<0)
                {
                    //printf("nme %i ai update\n",c);
                    a = ((rand()%256)/256.0)*M_PI*2;
                    nme[c]->vx=cos(a)*nme[c]->v;
                    nme[c]->vy=sin(a)*nme[c]->v;
                    nme[c]->ai=nme[c]->speed;
                }
                for ( i=0 , j=2; i<3;i++,j=i-1)
                {
                    d=distance(nme[c]->fx,nme[c]->fy,tri[i][0],tri[i][1]);
                    if (d<POINTDIST)
                    {
                        nme[c]->vx=nor[j][0]*NMEBOUNCE;
                        nme[c]->vy=nor[j][1]*NMEBOUNCE;
                        nme[c]->ai=10;
                    }
                }
                for ( i=0 , j=2; i<3; i++,j=i-1)
                {

                    d=line_distance(nme[c]->fx,nme[c]->fy,tri[i][0],tri[i][1],tri[j][0],tri[j][1]);
                    if (d<LINEDIST)
                    {
                        nme[c]->vx=nor[j][0]*nme[c]->v*2;
                        nme[c]->vy=nor[j][1]*nme[c]->v*2;
                        nme[c]->ai=10;
                        //nme[c]->hit=1;
                    }
                }
            }
        }

        nme[c]->x=nme[c]->fx;
        nme[c]->y=nme[c]->fy;

        nme[c]->trail[nme[c]->current_trail][0]=nme[c]->x;
        nme[c]->trail[nme[c]->current_trail][1]=nme[c]->y;
        nme[c]->current_trail++;
        if (nme[c]->current_trail>=TRAIL) nme[c]->current_trail=0;

    }
}

void update_rgn()
{
    int c;
    float f;
    if (revolve_interleave==REVOLVEINTERLEAVE || bullettime)
    {
        update_clouds();

    tri[0][0]=(cos(revolve)*TRIDIUS)+BASEW/2;
    tri[0][1]=(sin(revolve)*TRIDIUS)+BASEH/2;
    tri[1][0]=(cos(revolve+((M_PI*2)/3))*TRIDIUS)+BASEW/2;
    tri[1][1]=(sin(revolve+((M_PI*2)/3))*TRIDIUS)+BASEH/2;
    tri[2][0]=(cos(revolve+((M_PI*4)/3))*TRIDIUS)+BASEW/2;
    tri[2][1]=(sin(revolve+((M_PI*4)/3))*TRIDIUS)+BASEH/2;


    nor[0][0]=tri[2][0]-BASEW/2;
    nor[0][1]=tri[2][1]-BASEH/2;
    nor[1][0]=tri[0][0]-BASEW/2;
    nor[1][1]=tri[0][1]-BASEH/2;
    nor[2][0]=tri[1][0]-BASEW/2;
    nor[2][1]=tri[1][1]-BASEH/2;

    normalise(&(nor[0][0]),&(nor[0][1]));
    normalise(&(nor[1][0]),&(nor[1][1]));
    normalise(&(nor[2][0]),&(nor[2][1]));

        rgnpts[0].x=tri[0][0]*ZOOM+OFFSETX;
        rgnpts[0].y=tri[0][1]*ZOOM+OFFSETY;

        rgnpts[1].x=tri[1][0]*ZOOM+OFFSETX;
        rgnpts[1].y=tri[1][1]*ZOOM+OFFSETY;

        rgnpts[2].x=tri[2][0]*ZOOM+OFFSETX;
        rgnpts[2].y=tri[2][1]*ZOOM+OFFSETY;


    for (c=0;c<3;c++)
    {
         window.triangle[c][0]=rgnpts[c].x+window.rect.left;
         window.triangle[c][1]=rgnpts[c].y+window.rect.top;
        if (window.triangle[c][0]<window.screen.left) window.vx=player->ship->velocity;
        if (window.triangle[c][0]>window.screen.right) window.vx=-player->ship->velocity;

        if (window.triangle[c][1]<window.screen.top) window.vy=player->ship->velocity;
        if (window.triangle[c][1]>window.screen.bottom) window.vy=-player->ship->velocity;

    }
    if (window.vx!=0 || window.vy!=0)
    {
        window.x+=window.vx * (bullettime ? BULLETTIME : 1.0);
        window.y+=window.vy * (bullettime ? BULLETTIME : 1.0);
        window.rect.top=window.y;
        window.rect.bottom=window.y+window.h;
        window.rect.left=window.x;
        window.rect.right=window.x+window.w;
        MoveWindow(win_get_window(),window.rect.left,window.rect.top,window.w,window.h,1);
    }
    f=(bullettime ? (1.0-WINDOWDEC)*BULLETTIME : 0);
    window.vx*=WINDOWDEC+f;
    window.vy*=WINDOWDEC+f;


    if (!DEBUGMODE)
    {
        region = CreatePolygonRgn(rgnpts,3,WINDING);
        SetWindowRgn(win_get_window(), region, 1);
        DeleteObject(region);
    }

    revolve_interleave=0;
    } else revolve_interleave++;

}


////////////////////////////////////////////////////////////////////////////////   INTERRUPT

void timer_proc()
{
    //frames++;
    revolve+=REVOLVESPEED;
    update_rgn();
    update_shots(player);
    update_player(player);
    update_nme();
    /*
    if (!(frames%FPS))
    {
        fps=tempfps+1;
        tempfps=0;
    }*/
    if (key[KEY_SPACE]) shoot(player); else player->shotdelay=0;
    if (key[KEY_Z] && !bullettime)
    {
        install_int_ex(timer, BPS_TO_TIMER(FPS*BULLETTIME));
        bullettime=1;
    }
    if (!key[KEY_Z] && bullettime)
    {
        install_int_ex(timer, BPS_TO_TIMER(FPS));
        bullettime=0;
    }

    current_viewport=abs((mouse_z+3))%VIEWPORTS;
    //printf("Current viewport is %i at frame %i\n",current_viewport,frames);
}


////////////////////////////////////////////////////////////////////////////////   GENERATING AND LOADING


void create_enemies()
{
    int c;
    printf("hophop\n");
    for (c=0;c<NMETYPES;c++) nmetypes[c]=malloc(sizeof(ENEMY));
    printf("hophop\n");
    // enemy 0
    nmetypes[0]->x=0;
    nmetypes[0]->y=0;
    nmetypes[0]->frame=0;
    for (c=0;c<TRAIL;c++)
    {
        nmetypes[0]->trail[c][0]=0;
        nmetypes[0]->trail[c][1]=0;
    }
    nmetypes[0]->current_trail=0;
    nmetypes[0]->hit=0;

    nmetypes[0]->hp=200;
    nmetypes[0]->ai=0;
    nmetypes[0]->speed=50;
    nmetypes[0]->enter=1;
    nmetypes[0]->dead=1;

    nmetypes[0]->v=.5;
    load_sprites(nmetypes[0]->s,"roosvelzombie",2);
    printf("hophop\n");
    // enemy 1
    nmetypes[1]->x=0;
    nmetypes[1]->y=0;
    nmetypes[1]->frame=0;
    for (c=0;c<TRAIL;c++)
    {
        nmetypes[1]->trail[c][0]=0;
        nmetypes[1]->trail[c][1]=0;
    }
    nmetypes[1]->current_trail=0;
    nmetypes[1]->hit=0;

    nmetypes[1]->hp=200;
    nmetypes[1]->ai=0;
    nmetypes[1]->speed=10;
    nmetypes[1]->enter=1;
    nmetypes[1]->dead=1;

    nmetypes[1]->v=1.1;
    load_sprites(nmetypes[1]->s,"roosvelzombie",2);
    printf("hophop\n");
    for (c=0;c<ENOMY;c++)
    {
        nme[c] = malloc(sizeof(ENEMY));
        memcpy(nme[c],nmetypes[0],sizeof(ENEMY));
    }
    printf("hophop\n");
}

void create_ships()
{
    int c;
    for (c=0;c<MAXSHIPS;c++) ships[c]=malloc(sizeof(SHIP));
    // SHIP 0
    ships[0]->maxhp          =100;
    ships[0]->rapidfire      =0;
    ships[0]->bulletlife     =2000;
    ships[0]->price          =100;
    ships[0]->acceleration   =.6;
    ships[0]->velocity       =12.2;
    ships[0]->shotvelocity   =6.2;
    ships[0]->shotdamage     =20;
    ships[0]->accuracy       =0.01;
    ships[0]->s=load_sprite  ("ship");
    load_sprites(ships[0]->shot,"bullet",2);
    sprintf(ships[0]->name,"Cheetah");

    // SHIP 1
    ships[1]->maxhp          =100;
    ships[1]->rapidfire      =8;
    ships[1]->bulletlife     =20;
    ships[1]->price          =100;
    ships[1]->acceleration   =.2;
    ships[1]->velocity       =2.2;
    ships[1]->shotvelocity   =2.2;
    ships[1]->shotdamage     =80;
    ships[1]->accuracy       =.6;
    ships[1]->s=load_sprite  ("ship");
    load_sprites(ships[1]->shot,"bullet",2);
    sprintf(ships[1]->name,"Puma");
}

PLAYER *create_player()
{
    PLAYER *p;
    p=malloc(sizeof(PLAYER));
    p->x=BASEW/2;
    p->y=BASEH/2;
    p->fx=p->x;
    p->fy=p->y;
    p->vx=0;
    p->vy=0;
    p->ship=ships[0];
    p->hp=p->ship->maxhp;
    p->shotdelay=0;

    return p;
}

void load_shots(BULLET *shits,int num,const char* sprite)
{
    int c;
    //SPRITE *s[2];
    for (c=0;c<num;c++)
    {
        shits[c].on=0;
        //shits[c].s[0]=s[0];
        //shits[c].s[1]=s[1];
    }

}

void load_clouds(CLOUD *shits,int num,const char* sprite)
{
    int c;
    SPRITE *s[CLOUD_SPRITES];
    load_sprites(s,sprite,CLOUD_SPRITES);
    for (c=0;c<num;c++)
    {
        shits[c].y=2000;
        shits[c].s=s[rand()%CLOUD_SPRITES];
    }

}

void load_sounds()
{
    jingle = load_wav("snd/tune.wav");
    ship_ricochet = load_wav("snd/ship_ricochet.wav");
    nme_ricochet = load_wav("snd/nme_ricochet.wav");
    shoot_sound = load_wav("snd/shoot.wav");
    kaboom = load_wav("snd/explosion.wav");
    play_sample(jingle,255,128,1000,1);


}

void init_window()
{
    printf("Initialising dynamic window...\n");
    GetWindowRect(win_get_window(),&(window.rect));
    GetWindowRect(GetDesktopWindow(),&(window.screen));
    window.x=window.rect.left;
    window.y=window.rect.top;
    window.vx=0;
    window.vy=0;
    window.w=window.rect.right-window.rect.left;
    window.h=window.rect.bottom-window.rect.top;
    printf("Window initialised at [%i,%i] to [%i,%i]\n",window.rect.left,window.rect.top,window.rect.right,window.rect.bottom);
    printf("Widnow dimensions are %ix%i\n",window.w,window.h);
    printf("Screen resolution is %ix%i\n",window.screen.right,window.screen.bottom);
}


////////////////////////////////////////////////////////////////////////////////   INIT BATCH

void init()
{
    //player_bmp=load_bmp("ship.bmp",NULL);
    //player_shot[0]=load_bmp("shot1.bmp",NULL);
    //player_shot[1]=load_bmp("shot2.bmp",NULL);

    printf("Loading image files...\n");
    printf(" - player sprites\n");
    create_ships();
    load_sounds();
    create_enemies();
    player = create_player();
    printf(" - cloud sprites\n");
    load_clouds(clouds,MAXCLOUDS,"cloud");
    printf(" - bullet sprites\n");
    load_shots(shots,MAXSHOTS,"bullet");
    printf("Creating MRT\n");
    mrt=create_mrt();
    init_window();

    //dbg=load_sprite("ship");
    printf("Setting up viewports...\n");
    viewport[0]=mrt->albedo;
    viewport[1]=mrt->glow;
    viewport[2]=mrt->overlay;
    viewport[3]=mrt->render;
}


////////////////////////////////////////////////////////////////////////////////   INTERRUPT HANDLER (TBD)

////////////////////////////////////////////////////////////////////////////////   MAIN

int main(int argc, char *argv[])
{
    printf("*** Brain Damage v0.0.3 ***\n");
    printf("(c) sos 2010\n");
    printf("INITIALISING...\n");
    srand(time(NULL));

    printf("Initialising Allegro...");
    allegro_init();
    install_keyboard();
    install_timer();
    install_mouse();
    install_sound(DIGI_AUTODETECT, MIDI_NONE, argv[0]);
    set_color_depth(32);
    set_gfx_mode(GFX_AUTODETECT_WINDOWED, BASEW*ZOOM, BASEH*ZOOM, 0, 0);
    buffer = create_bitmap(BASEW, BASEH);

    printf("OK!\n");
    init();

    printf("Starting event thread...");
    install_int_ex(timer, BPS_TO_TIMER(FPS));
    LOCK_FUNCTION(timer);
    LOCK_VARIABLE(tim);

    printf("FULLY INITIALISED!");

    while (!key[KEY_ESC])
    {
        clear_to_color(buffer, makecol(48,192, 128));
        clear_mrt(mrt);
        while (tim >0)
        {
            timer_proc();
            tim--;
        }

        rysuj();
        //draw_sprite(viewport[current_viewport],mrt->overlay,0,0);
        stretch_blit(buffer,screen,0,0,BASEW,BASEH,0,0,BASEW*ZOOM,BASEH*ZOOM);
//        vsync();


    }

    remove_timer();
    remove_sound();
    allegro_exit();

    return 0;
}
END_OF_MAIN();
