#include "hge.h"
#include <vector>
#include "math.h"
#include "allinone.h"
HGE *hge = NULL;
hgeQuad bgquad;

cPlayer *player;
std::vector<cBullet *> bullets;
std::vector<cGameObj*> enemys;
std::vector<cBullet *> enbullets;
std::vector<cGameObj *> deadenemys;
HTEXTURE enemytex;
int state;
int level;
float test;
cSplashScr splash;

/***********************
//  functions
***********************/

inline float scrx(const float x){return x-camx;};
inline float scry(const float y){return y-camy;};

HTEXTURE drawPlayer(){
    const int xsize = 32;
    const int ysize = 32;
    const int plcol = ARGB(255,255,255,255);
    HTEXTURE tex = hge->Texture_Create(xsize,xsize);
    DWORD *pixel = hge->Texture_Lock(tex);
    int i, j;
    for(i=0; i<xsize;i++)
        for(j=0;j<ysize;j++) {
            if (          i< 8 and 15- 3<j and j<17+3)    pixel[i*xsize+j] = plcol; else
            if ( 8<=i and i<16 and 15- 6<j and j<17+6)    pixel[i*xsize+j] = plcol; else
            if (16<=i and i<24 and 15-9<j and j<17+9)     pixel[i*xsize+j] = plcol; else
            if (24<=i and i<32 and 15-12<j and j<17+12 and !(15-3<j and j<17+3))   pixel[i*xsize+j] = plcol; else
                pixel[i*xsize+j] = 0x00000000;
        }
    hge->Texture_Unlock(tex);
    return tex;
}
HTEXTURE drawBullet(){
    const int xsize = 16;
    const int ysize = 16;
    HTEXTURE tex = hge->Texture_Create(xsize,ysize);
    DWORD *pointer = hge->Texture_Lock(tex, false);
    int i, j;
    for(i=0; i<ysize;i++)
        for(j=0;j<xsize;j++) {
            if (6<i and i<9 and 2<j and j<13)
                pointer[i*xsize+j] = 0xFFFFFFFF; else
            if (5<i and i<10 and 1<j and j<14)
                pointer[i*xsize+j] = 0x80FFFFFF; else
            if (4<i and i<11 and 0<j and j<15)
                pointer[i*xsize+j] = 0x40FFFFFF; else
            if (3<i and i<12 and 0<j and j<15)
                pointer[i*xsize+j] = 0x20FFFFFF;
                }
    hge->Texture_Unlock(tex);
    return tex;
}
HTEXTURE drawTorpedo(){
    const int xsize = 16;
    const int ysize = 16;
    HTEXTURE tex = hge->Texture_Create(xsize,ysize);
    DWORD *pointer = hge->Texture_Lock(tex, false);
    int i, j;
    for(i=0; i<ysize;i++)
        for(j=0;j<xsize;j++) {
            if (6<i and i<9 and 6<j and j<9)
                pointer[i*xsize+j] = 0xFFFFFFFF; else
            if (5<i and i<10 and 5<j and j<10)
                pointer[i*xsize+j] = 0x80FFFFFF; else
            if (4<i and i<11 and 4<j and j<11)
                pointer[i*xsize+j] = 0x40FFFFFF; else
            if (3<i and i<12 and 3<j and j<12)
                pointer[i*xsize+j] = 0x20FFFFFF;
                }
    hge->Texture_Unlock(tex);
    return tex;
}
HTEXTURE drawBGLayer(const int type = 0){
    //Tfractals;
    HTEXTURE tex = hge->Texture_Create(512,512);
    DWORD *pixel = hge->Texture_Lock(tex, false);

    DWORD col = type ? ARGB(200,128,128,128) : ARGB(128,128,128,128);;
    DWORD tmp[4][4] = {
        {0,     col,    col ,   0},
        {col,   0,      0,      col},
        {col,   0,      0,      col},
        {0,     col,    col,    0}      };
    DWORD tmp2[4][4] = {
        {0,     0,      0,   0},
        {0,     col,    col,    0},
        {0,   col,      0,     0},
        {0,     0,    0,    0}      };

    hgeColorRGB texcol, mixcol, resultcol;

    for (int step=1; step<=8; step++)  {
        hge->System_Log("prepare fractal");
        int size = pow(2, step+1);
        int i,j,k,l;
        for (i=0,k=0; i<512; i++, k++)
            for (j=0, l=0; j<512; j++, l++){
                if (k==size) k=0;
                if (l==size) l=0;
                texcol.SetHWColor(pixel[i*512+j]);
                if (type != 0)
                    mixcol.SetHWColor(tmp[4*k/size][4*l/size]);
                else
                    mixcol.SetHWColor(tmp2[4*k/size][4*l/size]);
                resultcol = texcol +mixcol.a* mixcol;

                pixel[i*512+j] = //tmp[4*k/size][4*l/size];
                    ARGB (
                    (int)(255*resultcol.a),
                    (int)(255*resultcol.r),
                    (int)(255*resultcol.g),
                    (int)(255*resultcol.b)   );
            }
        hge->System_Log("...Finish");
    }
    hge->Texture_Unlock(tex);
    return tex;
}
HTEXTURE drawenemy1(){
    const int size = 32;
    HTEXTURE tex = hge->Texture_Create(size,size);
    DWORD *pixel = hge->Texture_Lock(tex, false);
    DWORD tmp[8][8] = {
        {0,0,0,1,1,0,0,0},
        {0,0,1,1,1,1,0,0},
        {0,1,1,1,1,1,1,0},
        {0,0,0,1,1,0,0,0},
        {0,0,1,1,1,1,0,0},
        {0,1,1,1,1,1,1,0},
        {1,1,1,1,1,1,1,1},
        {1,1,1,0,0,1,1,1} };
    int i,j;
    for (i=0; i<32; i++)
        for (j=0; j<32; j++)
            pixel[i*size+j] = (tmp[8*i/size][8*j/size] == 0) ? 0: 0xFFFFFFFF;

    hge->Texture_Unlock(tex);
    return tex;
}
HTEXTURE drawenemy2(){
    const int size = 64;
    HTEXTURE tex = hge->Texture_Create(size,size);
    DWORD *pixel = hge->Texture_Lock(tex, false);
    DWORD tmp[8][8] = {
        {0,0,0,1,1,0,0,0},
        {0,0,1,1,1,1,0,0},
        {0,1,1,1,1,1,1,0},
        {1,1,1,1,1,1,1,1},
        {1,1,1,1,1,1,1,1},
        {0,1,1,1,1,1,1,0},
        {0,0,1,1,1,1,0,0},
        {0,0,0,1,1,0,0,0} };
    int i,j;
    for (i=0; i<64; i++)
        for (j=0; j<64; j++)
            pixel[i*size+j] = tmp[8*i/size][8*j/size] == 0 ? 0: 0xFFFFFFFF;

    hge->Texture_Unlock(tex);
    return tex;
}
HTEXTURE drawarrow(){
    const int size = 16;
    HTEXTURE tex = hge->Texture_Create(size,size);
    DWORD *pixel = hge->Texture_Lock(tex, false);
    DWORD tmp[8][8] = {
        {0,0,0,1,1,0,0,0},
        {0,0,1,1,1,1,0,0},
        {0,1,1,0,0,1,1,0},
        {1,1,0,0,0,0,1,1},
        {1,0,0,0,0,0,0,1},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0} };
    int i,j;
    for (i=0; i<16; i++)
        for (j=0; j<16; j++)
            pixel[i*size+j] = (tmp[8*i/size][8*j/size] == 0) ? 0: 0xFFFFFFFF;

    hge->Texture_Unlock(tex);
    return tex;
}
inline void _render(hgeSprite *spr, const float x, const float y, const float rot, const float scale){
    spr->RenderEx(scrx(x), scry(y), rot, scale == -1 ? 1 : scale, scale == -1 ? 1 : scale);
};
hgeSprite *makespr(const HTEXTURE tex, const float w, const float h){
    hgeSprite *spr = new hgeSprite(tex, 0,0, w, h);
    spr->SetHotSpot(w/2, h/2);
    return spr;
}
void quadinit(hgeQuad *_quad){
    _quad->blend = BLEND_ALPHABLEND | BLEND_COLORMUL | BLEND_NOZWRITE;
    for(int i=0;i<4;i++) {
        _quad->v[i].z=0.5f;
        _quad->v[i].col=ARGB(255, 255, 255, 255);
    }
    _quad->v[0].x=0;        _quad->v[0].y=0;
    _quad->v[1].x=scrWidth; _quad->v[1].y=0;
    _quad->v[2].x=scrWidth; _quad->v[2].y=scrHeight;
    _quad->v[3].x=0;        _quad->v[3].y=scrHeight;
}
void drawbg(){
    float tx, ty, texsize;

    texsize = 512;
    tx = camx/texsize; ty = camy/texsize;
    bgquad.v[0].tx=tx;                     bgquad.v[0].ty=ty;
    bgquad.v[1].tx=tx+scrWidth/texsize;    bgquad.v[1].ty=ty;
    bgquad.v[2].tx=tx+scrWidth/texsize;    bgquad.v[2].ty=ty+scrHeight/texsize;
    bgquad.v[3].tx=tx;                     bgquad.v[3].ty=ty+scrHeight/texsize;

    hge->Gfx_RenderQuad(&bgquad);
}
void drawinter(){
    fnt1->printf(5, 5, HGETEXT_LEFT, "SCORE: %d", scorecount);
    if (player->health < plmaxhealth) fnt1->printf(775, 5, HGETEXT_LEFT, "HEALTH:  %d (%2.2f sec)", player->health, player->hregen);
    else fnt1->printf(800, 5, HGETEXT_LEFT, "HEALTH:  %d", player->health);

    fnt2->printf(5, 30, HGETEXT_LEFT, "FPS:%d", hge->Timer_GetFPS());

    if (level !=1) {
    if (player->torpcd<0)
        fnt2->printf(512, 750, HGETEXT_MIDDLE, "superweapon ready!");
    else
        fnt2->printf(512, 750, HGETEXT_MIDDLE, "recharge: %1.1f", player->torpcd);
    } else {
        fnt2->printf(512, 750, HGETEXT_MIDDLE, "use your force!");
    }

    if (enemys.empty())
        fnt1->printf(512-50, 384, HGETEXT_MIDDLE, "L E V E L  %d", level+1);
    else fnt2->printf(512, 5, HGETEXT_MIDDLE, "level %d", level);

    if (state == 3)
        if (player->deathtimer < 0)
            fnt1->printf(512-100, 384, HGETEXT_MIDDLE, "G A M E   O V E R");
}
void scoreupdate(const float dt){
    scoretimer -= dt;
    if (scorecount != score and scoretimer < 0) {
        scorecount += scorecount < score ? 1:-1;
        scoretimer = 0.1;
    }
}
void loadRes(){
    camx = camy = 0; camz = 1;
    scrWidth = 1024; scrHeight = 768;
    level = 0;
    score = 0; scorecount = 0; scoretimer = 0;
    state = 1;

    bgquad.tex = drawBGLayer();
    quadinit(&bgquad);

    fnt1 = new hgeFont("Res\\font1.fnt");
    fnt2 = new hgeFont("Res\\font2.fnt");

    sprPlayer = makespr(drawPlayer(),32,32);
    sprPlayer->SetColor(plcolor);
    sprPlShw = makespr(drawPlayer(),32,32);
    sprPlShw->SetColor(shwcolor);
    sprBullet = makespr(drawBullet(),16,16);

    sprTorpedo = makespr(drawTorpedo(),16,16);
    sprTorpedo->SetColor(ARGB(255, 0, 255, 0));
    enemytex = drawBGLayer(1);
    sprEnemy1 = makespr(drawenemy1(),32,32);
    sprEnemy1->SetColor(enemycolor);
    sprEnShw1 = makespr(drawenemy1(),32,32);
    sprEnShw1->SetColor(shwcolor);

    sprEnemy2 = makespr(drawenemy2(),64,64);
    sprEnemy2->SetColor(enemycolor);
    sprEnShw2 = makespr(drawenemy2(),64,64);
    sprEnShw2->SetColor(shwcolor);

    sprArrow = makespr(drawarrow(),16,16);
    sprArrow->SetColor(enemycolor);

    HTEXTURE tex1 = hge->Texture_Load("Res\\idgc_logo.png");
    sprSplashScr = new hgeSprite(tex1,0,0, 1124, 1124);
    sprSplashScr->SetHotSpot(562,562);

    sndPlayerShoot = hge->Effect_Load("Res\\laser.wav");

    sndEnemyShoot = hge->Effect_Load("Res\\maser.wav");

    restartgame();
}
void restartgame(){
    camx = camy = 0; camz = 1;
    scrWidth = 1024; scrHeight = 768;
    level = 0;
    score = 0; scorecount = 0; scoretimer = 0;
    state = 1;

    delete player;
    player = new cPlayer();

    for (int i=0, max = bullets.size(); i<max; i++)
        delete bullets[i];
    for (int i=0, max = enemys.size(); i<max; i++)
        delete enemys[i];
    for (int i=0, max = enbullets.size(); i<max; i++)
        delete enbullets[i];
    for (int i=0, max = deadenemys.size(); i<max; i++)
        delete deadenemys[i];

    bullets.clear();
    enemys.clear();
    enbullets.clear();
    deadenemys.clear();
}
void bullethit(){
    hgeVector dist;
    int i, j, ibul = bullets.size(), ien = enemys.size();

    for (i = 0; i<ibul; i++)
        for (j=0; j<ien; j++){
                dist = bullets[i]->coord - enemys[j]->coord;
                if ( ((enemys[j]->type() == 1) ? (dist.Length()<20) : (dist.Length()<40)) and !bullets[i]->dead()){
                    bullets[i]->health = 0;
                    if (1== bullets[i]->type()) enemys[j]->health--;
                        else enemys[j]->health=0;}}


    for(i=0, ibul = enbullets.size(); i<ibul;i++){
        dist = enbullets[i]->coord - player->coord;
        if ((dist.Length()<20) and !enbullets[i]->dead()){
            enbullets[i]->health = 0;
            player->health--;    }}
}
void lvlspawn(const int num){
    if (num <= 1) enspawn(1);
    else {
        for(int i=0; i<num-1;i++){
            enspawn(1); enspawn(1);
            enspawn(2);
        }
    }
}
void enspawn(const int type, const bool bSetCoord, const float _x, const float _y){
    float dist = level <=1 ? 300 : 900;
    if (type == 1)
    {
        cEnemy1 *en = new cEnemy1();
        float rnd = hge->Random_Float(0, 2*M_PI);
        if (bSetCoord) {
            en->coord.x = _x;
            en->coord.y = _y;
        } else {
            en->coord.x = player->coord.x +dist*cos(rnd);
            en->coord.y = player->coord.y +dist*sin(rnd);
        }
        enemys.push_back(en);
    } else {
        cEnemy2 *en2 = new cEnemy2();
        float rnd2 = hge->Random_Float(0, 2*M_PI);
        if (bSetCoord) {
            en2->coord.x = _x;
            en2->coord.y = _y;
        } else {
            en2->coord.x = player->coord.x +dist*cos(rnd2);
            en2->coord.y = player->coord.y +dist*sin(rnd2);
        }
        enemys.push_back(en2);
    }
}
void bulletupdate(const float dt){
    for (int i =0, max = bullets.size(); i<max;i++){
        bullets[i]->update(dt);
        if (bullets[i]->dead()){
            bullets.erase(bullets.begin()+i);
            --max;}}

    for (int i =0, max = enbullets.size(); i<max;i++){
        enbullets[i]->update(dt);
        if (enbullets[i]->dead()){
            enbullets.erase(enbullets.begin()+i);
            --max;}}
}
void enupdate(const float dt){
    for (int i =0, max = enemys.size(); i<max;i++){
        enemys[i]->update(dt);
        if (enemys[i]->dead()){
            score += enemys[i]->type() == 1 ? 20:100;
            deadenemys.push_back(enemys[i]);
            enemys.erase(enemys.begin()+i);
            --max;}}

    for (int i =0, max = deadenemys.size(); i<max;i++) {
        if (deadenemys[i]->deathanim(dt)) {
            delete deadenemys[i];
            deadenemys.erase(deadenemys.begin()+i);
            --max;
        }
    }
}
inline float convangle(float angle){
    for(;angle > M_PI; angle -= 2*M_PI);
    for(;angle <-M_PI; angle += 2*M_PI);
    return angle;
}

/***********************
//  cSplashScr
***********************/

cSplashScr::cSplashScr() {timer = 0; timerstart = 0;}
cSplashScr::~cSplashScr(){}
void cSplashScr::render(){
    sprSplashScr->Render(scrWidth/2,scrHeight/2);
    if (timer >2) {
        fnt1->printf(scrWidth/2, scrHeight/2, HGETEXT_CENTER, "AFTERSHOCK");
        if (timerstart < 1.0)
            fnt1->printf(scrWidth/2, scrHeight - 50, HGETEXT_CENTER, "PRESS ENTER TO START");
    }

};
bool cSplashScr::update(const float dt){
    timer+=dt;
    if (timer <=2 )
        sprSplashScr->SetColor(ARGB((timer-2)*127,
                                    255,255,255)); else
    {
        timerstart +=dt;
        if (timerstart > 2) timerstart = 0;
    };

    return (timer > 2 and hge->Input_GetKeyState(HGEK_ENTER)) ?
        true : false;
};
void cSplashScr::restart() {timer =0; timerstart =0;};

/***********************
//  cGameObj
***********************/
cGameObj::cGameObj(){}
cGameObj::~cGameObj(){}
void cGameObj::render(){}
void cGameObj::update(float dt){}

/***********************
//  cPlayer
***********************/
cPlayer::cPlayer(){
    coord.x = scrWidth/2.0; coord.y = scrHeight/2.0;
    direct.x = 1; direct.y = 0;
    speed.x = 0; speed.y = 0;
    cdtimer = 0;
    health = plmaxhealth;
    hregen = plhregen;
    torpcd = 0;
    deathtimer = 5;
};
cPlayer::~cPlayer(){};
void cPlayer::update(const float dt){
    //rotate
    if (hge->Input_GetKeyState(HGEK_A) or hge->Input_GetKeyState(HGEK_LEFT))
        direct.Rotate(-rotatespeed*((maxspeed-speed.Length()*rotdownpercent/100)/maxspeed)*dt);

    if (hge->Input_GetKeyState(HGEK_D) or hge->Input_GetKeyState(HGEK_RIGHT))
        direct.Rotate(+rotatespeed*((maxspeed-speed.Length()*rotdownpercent/100)/maxspeed)*dt);

    //trotle
    hgeVector totalaccel;
    hgeVector friction;
    totalaccel.x =0; totalaccel.y = 0;

    if (hge->Input_GetKeyState(HGEK_W) or hge->Input_GetKeyState(HGEK_UP))    {
        totalaccel = direct;
        totalaccel.Normalize();
        totalaccel *= boataccel;
    }

    friction = -speed;
    friction.Normalize();
    if (speed.Length() < 0.2*maxspeed ) friction *= boataccel*0.2;
        else friction *= boataccel * (speed.Length()/maxspeed);

    totalaccel += friction;

    speed += totalaccel*dt;
    coord += speed*dt;

    //shoot
    if (hge->Input_GetKeyState(HGEK_CTRL))
        shoot(); else torpcd -=dt;
    cdtimer -=dt;

    if (hge->Input_GetKeyState(HGEK_SPACE) and level !=1)
        torpedoshoot();

    //camera update
    camx = (coord.x-scrWidth/2) + direct.x*scrHeight/2.2*speed.Length()/maxspeed;
    camy = (coord.y-scrHeight/2)+ direct.y*scrHeight/2.2*speed.Length()/maxspeed;

    //health
    if (health < plmaxhealth){
        hregen -=dt;
        if (hregen < 0) {
            health++;
            hregen = plhregen;
        }
    }
}
void cPlayer::shoot(){
    if (cdtimer<0) {
        cdtimer = plcooldown;
        hgeVector pos = coord;
        hgeVector dir = direct;
        dir.Normalize();
        dir*=16;
        pos+=dir;
        cBullet *bul = new cBullet(pos,direct, plcolor);
        bullets.push_back(bul);
        hge->Effect_Play(sndPlayerShoot);
    }
}
void cPlayer::torpedoshoot(){
    if (torpcd<0) {
        torpcd = pltorpedocd;
        hgeVector pos = coord;
        hgeVector dir = direct;
        dir.Normalize();
        dir*=16;
        pos+=dir;
        cTorpedo *bul = new cTorpedo(pos,direct, plcolor);
        bullets.push_back(bul);
        hge->Effect_Play(sndPlayerShoot);
    }
}
bool cPlayer::dead(){ return health <=0 ? true:false;}
void cPlayer::render(){
    if (!dead()) {
    _render(sprPlShw, coord.x+10, coord.y+10, direct.Angle()+M_PI_2);
    _render(sprPlayer, coord.x, coord.y, direct.Angle()+M_PI_2);
    } else _render(sprPlayer, coord.x, coord.y, direct.Angle()+M_PI_2, deathtimer/5);
}
bool cPlayer::deathanim(const float dt){
    if (deathtimer < 0) return true;
    else {
        direct.Rotate(0.5*rotatespeed*dt);
        coord+=direct*50*dt;
        deathtimer -=dt;
        return false;
    }
}


/***********************
//  cBullet
***********************/

cBullet::cBullet(const hgeVector pos, const hgeVector dir, DWORD _color){
    coord = pos;
    direct = dir;
    direct.Normalize();
    livetime = bulletlivetime;
    health = 1;
    color = _color;
}
cBullet::~cBullet(){}
void cBullet::render(){
    sprBullet->SetColor(color);
    _render(sprBullet, coord.x, coord.y, direct.Angle());
}
void cBullet::update(const float dt){
    coord += direct*dt * (color != plcolor ? enbulletspeed:bulletmaxspeed);
    livetime -= dt;}
bool cBullet::dead(){ return (livetime<0 or health <=0) ? true : false;}
int cBullet::type() {return 1;}


cTorpedo::cTorpedo(const hgeVector pos, const hgeVector dir, DWORD _color){
    coord = pos;
    direct = dir;
    direct.Normalize();
    livetime = torplivetime;
    health = 1;
    color = _color;
}
cTorpedo::~cTorpedo(){};
void cTorpedo::render(){
    sprTorpedo->RenderEx(scrx(coord.x), scry(coord.y), direct.Angle(), 3);
}
void cTorpedo::update(const float dt) {
    coord += direct*dt * torpspeed;
    livetime -= dt;
}
bool cTorpedo::dead(){ return (livetime<0 or health <=0) ? true : false;}
int cTorpedo::type() {return 2;}

/***********************
//  cEnemy1
***********************/

cEnemy1::cEnemy1(){
    direct.x=0;
    direct.y=1;
    health = enemy1health;
    cdtimer = 0;
    deathtimer = 5;
}
cEnemy1::~cEnemy1(){}
void cEnemy1::render(){
    float x = scrx(coord.x) , y = scry(coord.y);

    if (!dead()) {
    if (-16 < x and x < scrWidth+16 and
        -16 < y and y < scrHeight)
        {
            _render(sprEnShw1, coord.x+10, coord.y+10, direct.Angle()+M_PI_2);
            _render(sprEnemy1, coord.x, coord.y, direct.Angle()+M_PI_2);
        }
    else {
        float rot =0;
        if (x < -16) {x = 8; rot = -M_PI_2;} else
        if (x > scrWidth + 16) {x = scrWidth - 8; rot = M_PI_2;};

        if (y < -16) {y =  8; if (rot!=0) rot = rot < 0 ? -M_PI_4 : M_PI_4; } else
        if (y > scrHeight + 16) {y = scrHeight - 8; if (rot!=0) rot = rot < 0 ? -M_PI_2-M_PI_4 : M_PI_2+M_PI_4; else rot = M_PI; };

        sprArrow->RenderEx(x,y,rot);
    }
    } else {
        _render(sprEnemy1, coord.x, coord.y, direct.Angle()+M_PI_2, deathtimer/5);
    }
}
void cEnemy1::shoot(){
    if (cdtimer < 0){
        cdtimer = enemy1cd;
        hgeVector pos = coord;
        hgeVector dir = direct;
        dir.Normalize();
        dir*=32;
        pos+=dir;
        cBullet *bul = new cBullet(pos,direct, enemycolor);
        enbullets.push_back(bul);
        hge->Effect_Play(sndEnemyShoot);
    }
}
void cEnemy1::update(const float dt){
    hgeVector delta = player->coord - coord;
    float angle = convangle(delta.Angle() -direct.Angle());
    float rotspeed = enemy1rotate*(1-0.9*speed.Length()/enemy1speed);
    direct.Rotate(rotspeed*dt* (angle>0?1:-1));


    hgeVector totalaccel(0,0);
    hgeVector friction;

    if (!(angle > M_PI_2 or angle <-M_PI_2))    {
        totalaccel = direct;
        totalaccel.Normalize();
        totalaccel *= enemy1accel;
    }

    friction = -speed;
    friction.Normalize();
    if (speed.Length() < 0.2*enemy1speed ) friction *= enemy1accel*0.2;
        else friction *= enemy1accel* (speed.Length()/enemy1speed);

    totalaccel += friction;
    speed += totalaccel*dt;

    coord+=speed*dt;

    //
    cdtimer-=dt;
    if (abs(angle) < M_PI*15/180 and delta.Length()<400 and state == 2 ) shoot();
    }
int cEnemy1::type(){return 1;}
bool cEnemy1::dead(){return (health <= 0) ? true : false;}
bool cEnemy1::deathanim(const float dt){
    if (deathtimer < 0) return true;
    else {
        direct.Rotate(enemy1rotate*dt);
        coord+=direct*0.5*enemy1speed*dt;
        deathtimer -=dt;
        return false;
    }
}

/***********************
//  cEnemy2
***********************/

cEnemy2::cEnemy2(){
    direct.x=0;
    direct.y=1;
    health = enemy2health;
    memplcoord = player->coord;
    cdtimer = 0;
    ammo=3;
    spawntimer = enemy2spawn1;
    spawntimer2 = enemy2spawn2;
    deathtimer = 5;
}
cEnemy2::~cEnemy2(){}
void cEnemy2::render(){
    float x = scrx(coord.x) , y = scry(coord.y);
    if (!dead())
    if (-32 < x and x < scrWidth+32 and
        -32 < y and y < scrHeight)
        {
            _render(sprEnShw2, coord.x+10, coord.y+10, rot);
            _render(sprEnemy2, coord.x, coord.y, rot);
        }
    else {
        float rot =0;
        if (x < -32) {x = 8; rot = -M_PI_2;} else
        if (x > scrWidth + 32) {x = scrWidth - 8; rot = M_PI_2;};

        if (y < -32) {y =  8; if (rot!=0) rot = rot < 0 ? -M_PI_4 : M_PI_4; } else
        if (y > scrHeight + 32) {y = scrHeight - 8; if (rot!=0) rot = rot < 0 ? -M_PI_2-M_PI_4 : M_PI_2+M_PI_4; else rot = M_PI;};

        sprArrow->RenderEx(x,y,rot);
    } else {
        _render(sprEnemy2, coord.x, coord.y, rot, deathtimer/5);
    }
}
void cEnemy2::shoot(){
    bool produce = false;

    if (cdtimer < -0.5){
        cdtimer = enemy2cd;
        ammo = 3;
        produce = true;
    } else if (cdtimer < -0.25 and ammo == 2){
        ammo--;
        produce = true;
    } else if (cdtimer < 0 and ammo == 3) {
        ammo--;
        produce = true;
    }

    if (produce) {
        hgeVector pos = coord;
        hgeVector dir = player->coord - coord;
        dir.Normalize();
        dir*=32;
        pos+=dir;
        cBullet *bul = new cBullet(pos,dir, enemycolor);
        enbullets.push_back(bul);
        hge->Effect_Play(sndEnemyShoot);
    }
}
void cEnemy2::update(const float dt){
    rot += rot < M_PI*2 ? enemy2autorot*dt : enemy2autorot*dt-M_PI*2;
    hgeVector delta = memplcoord - coord;
    float angle = convangle(delta.Angle()-direct.Angle());
    //float rotspeed = enemy2rotate*(1-0.9*speed.Length()/enemy2speed);
    if (speed.Length() < 0.01*enemy2speed){
        direct.Rotate(enemy2rotate*dt* (angle>0?1:-1));
        memplcoord = player->coord;
    }

    hgeVector totalaccel(0,0);
    hgeVector friction;

    if (!(angle > M_PI_2/90 or angle <-M_PI_2/90))    {
        totalaccel = direct;
        totalaccel.Normalize();
        totalaccel *= enemy2accel;
    }

    friction = -speed;
    friction.Normalize();
    if (speed.Length() < 0.2*enemy2speed ) friction *= enemy2fric*0.2;
        else friction *= enemy2fric* (speed.Length()/enemy2speed);

    totalaccel += friction;
    speed += totalaccel*dt;
    coord+=speed*dt;

    //shoot
    cdtimer-=dt;
    if (delta.Length()<700 and state == 2) shoot();

    //spawn
    spawntimer -= dt;
    spawntimer2 -= dt;
    if (state == 2) spawn();
}
bool cEnemy2::dead(){return (health <= 0) ? true : false;}
int cEnemy2::type(){return 2;}
bool cEnemy2::deathanim(const float dt){
    if (deathtimer < 0) return true;
    else {
        coord+=direct*50*dt;
        deathtimer -=dt;
        return false;
    }
}

void cEnemy2::spawn(){
    if (spawntimer2 < 0 and level >= 5){
        spawntimer2 = enemy2spawn2;
        spawntimer = enemy2spawn1;
        enspawn(2, true, coord.x, coord.y);
    }
    if (spawntimer < 0 and level >= 3 ) {
        enspawn(1, true, coord.x, coord.y);
        spawntimer = enemy2spawn1;
    }

}

//---------------------------

bool FrameFunc() {
    float dt = hge->Timer_GetDelta();
    if (hge->Input_KeyDown(HGEK_BACKSPACE)) {hge->System_Snapshot();}
    switch (state)
    {
    	case 1: if (splash.update(dt)) state = 2;
            if (hge->Input_KeyDown(HGEK_ESCAPE)) return true;
    	break;
    	case 2:
            player->update(dt);
            bullethit();
            bulletupdate(dt);
            enupdate(dt);

            if (enemys.empty() and deadenemys.empty()) lvlspawn(++level);
            scoreupdate(dt);

            if (hge->Input_KeyDown(HGEK_ESCAPE)) {
                splash.restart();
                restartgame();
                state=1;};
            if (player->health <=0)
                state = 3;
    	break;
        case 3:
            bulletupdate(dt);
            enupdate(dt);
            player->deathanim(dt);
            if (hge->Input_KeyDown(HGEK_ESCAPE) and player->deathtimer < 0) {
                splash.restart();
                restartgame();
                state=1;};
        break;

    	default:
    		break;
    }
    return false;
}

bool RenderFunc() {
    hge->Gfx_BeginScene();
    hge->Gfx_Clear(0);
    switch (state)
    {
    	case 1:
    	splash.render();
    		break;
        case 2:
            drawbg();
            for (int i =0, max = deadenemys.size();  i<max;i++) deadenemys[i]->render();
            player->render();
            for (int i =0, max = bullets.size(); i<max;i++) bullets[i]->render();
            for (int i =0, max = enbullets.size(); i<max;i++) enbullets[i]->render();
            for (int i =0, max = enemys.size();  i<max;i++) enemys[i]->render();
            drawinter();
        break;

        case 3:
    		drawbg();
    		for (int i =0, max = deadenemys.size();  i<max;i++) deadenemys[i]->render();
            if (player->deathtimer > 0)
                player->render();
            for (int i =0, max = bullets.size(); i<max;i++) bullets[i]->render();
            for (int i =0, max = enbullets.size(); i<max;i++) enbullets[i]->render();
            for (int i =0, max = enemys.size();  i<max;i++) enemys[i]->render();
            drawinter();
    		break;

    	default:
    		break;
    }
    hge->Gfx_EndScene();
    return false;
}

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    hge = hgeCreate(HGE_VERSION);
    hge->System_SetState(HGE_TITLE, "Aftershock");
    hge->System_SetState(HGE_FRAMEFUNC, FrameFunc);
    hge->System_SetState(HGE_RENDERFUNC, RenderFunc);
    hge->System_SetState(HGE_WINDOWED, true);
    hge->System_SetState(HGE_LOGFILE, "log.txt");
    hge->System_SetState(HGE_SCREENWIDTH, 1024);
    hge->System_SetState(HGE_SCREENHEIGHT, 768);
    hge->System_SetState(HGE_SHOWSPLASH,false);
    //hge->System_SetState(HGE_FPS, 50);

    if (hge->System_Initiate()) {
        loadRes();
        hge->System_Start();
    }
    hge->System_Shutdown();
    return 0;
}
