#include <easyx.h>
#include <time.h>
#include <conio.h>

// 公共基类,可以做高级抽象
struct Cell
{
    int x, y;
    void __update(int deltaX, int deltaY)
    {
        x += deltaX;
        y += deltaY;
    }
};

// 子弹
class Bullet : public Cell
{
    typedef Cell    Base;
public:
    clock_t d;          // 记录更新时刻
    bool on = false;    // 子弹对象是否消亡了
    int  move = 3;      // 移动速度
    COLORREF col;       // 子弹颜色

    // 画出新的位置
    // now: 当前这张图像的时刻
    virtual void show(clock_t now)
    {
        setfillcolor(col); //col: 颜色
        fillrectangle(x - 5, y - 5, x + 5, y + 5);  //使用用上边设置填充颜色填充出来一个方块
    }
    // 更新子弹位置并进行位置约束,返回是否消亡
    // leftX,rightX: 左右限位坐标
    virtual bool update(clock_t now, int leftX, int rightX)
    {
        // 刷新率控制
        del();
        if (now - d <= 20) return false;

        do{
            Base::__update(move, 0);

            // 边界检测
            if (this->x <= leftX) break;
            if (this->x >= rightX) break;

            // 记录时间
            d = now;
            return true;
        } while (0);

        // 死亡了
        this->on = false;
        return false;
    }

private:
    //覆盖原来的位置
    void del()
    {
        setfillcolor(0); //0: 黑色
        setlinecolor(0); //0: 黑色
        fillrectangle(x - 5, y - 5, x + 5, y + 5);  //使用用上边设置填充颜色填充出来一个方块
        rectangle(x - 5, y - 5, x + 5, y + 5);      //使用用上边设置的线颜色画一个方框
    }
};

// 物体
class Body : public Cell
{
public:
    bool        hurting = false;    //标志是否受伤了
    int         hp = 100;           //生命
    COLORREF    clr;                //物体颜色
    clock_t     d_hurt;             //受伤的时刻
    clock_t     mDelay = clock();   //移动的延时
    clock_t     mDelayAtt = clock();//开火的延时

    // 受伤计算
    virtual bool hurt(clock_t now, int)
    {
        d_hurt = now;
        hurting = true;
        return (hp <= 0);
    }
    // 发射子弹
    virtual void fire(clock_t now, Bullet& but)
    {
        //放置一个子弹
        but.on = true;
        but.x = x;
        but.y = y;
        but.d = now;
    }
};

//敌人
class Boss : public Body
{
    typedef Body    Base;
public:

    bool angle = false;//方向
    // 移动更新
    // upY,downY: Y轴 移动位置限制
    // speed: 移动速度(小于0:上移. 大于0:下移.)
    virtual void update(clock_t now, int upY, int downY, int speed)
    {
        del();
        if (now - this->mDelay < 50) return;
        this->mDelay = now;

        //上上下下得移动
        if (angle == true)
            Base::__update(0, -speed);
        else
            Base::__update(0, speed);

        // 达到边界上就反向移动
        if (y >= downY)
            angle = true;
        if (y <= upY)
            angle = false;
    }

    // 图像显示
    virtual void show(clock_t now)
    {
        //受伤闪烁0.1秒
        if (hurting == true){
            if (now - d_hurt > 100){
                hurting = false;
                clr = RGB(150, 180, 210);
            }
            else
                clr = RGB(255, 0, 0);
        }
        setfillcolor(clr);
        fillrectangle(x - 20, y - 40, x + 20, y + 40);
    }

    virtual void fire(clock_t now, Bullet& but)//攻击
    {
        Base::fire(now, but);
        but.x -= 20;
        but.col = RGB(255, 180, 20);
        but.move = -3;
    }

    virtual bool hurt(clock_t now, int repeat) //受伤
    {
        hp -= 4 * repeat;
        setfillcolor(0);
        setlinecolor(WHITE);
        fillrectangle(160, 485, 560, 510);//更新血条
        rectangle(160, 485, 160 + hp * 4, 510);
        setfillcolor(RGB(230, 0, 1));
        setlinecolor(RGB(255, 255, 255));
        fillrectangle(160, 485, 160 + hp * 4, 510);
        rectangle(160, 485, 160 + hp * 4, 510);
        //返回是否死亡
        return Body::hurt(now, repeat);
    }

protected:
    void del()
    {
        setfillcolor(0);
        setlinecolor(0);
        rectangle(x - 20, y - 40, x + 20, y + 40);
        fillrectangle(x - 20, y - 40, x + 20, y + 40);
    }
};

//玩家类,同上
class Tank : public Body
{
    typedef Body    Base;
public:
    // 更新
    virtual void update(clock_t now, int upY, int downY, int speed)
    {
        //擦除图案
        del();

        // 更新周期计算
        if (now - this->mDelay < 40) return;
        this->mDelay = now;

        // 移动限位
        if (speed == 0) return;
        if (speed < 0 && y <= upY) return;
        if (speed > 0 && y >= downY) return;

        //更新
        Base::__update(0, speed);
    }

    virtual void show(clock_t now)
    {
        //受伤闪烁0.1秒
        if (hurting == true){
            if (now - d_hurt > 100){
                hurting = false;
                clr = RGB(0, 130, 125);
            }
            else
                clr = RGB(0, 255, 0);
        }

        setfillcolor(clr);
        fillrectangle(x - 25, y - 25, x + 25, y + 25);
        setfillcolor(RGB(100, 200, 180));
        fillrectangle(x, y + 5, x + 40, y - 5);
    }
    virtual void fire(clock_t now, Bullet& but)
    {
        Base::fire(now, but);
        but.x += 45; // 修正位置
        but.col = RGB(150, 180, 210);
        but.move = 3;
    }

    // repeat: 击中了几下
    virtual bool hurt(clock_t now, int repeat)
    {
        hp -= 2* repeat;
        setfillcolor(0);
        setlinecolor(WHITE);
        fillrectangle(160, 515, 560, 540);
        rectangle(160, 515, 560, 540);
        rectangle(160, 515, 160 + hp * 4, 540);
        setfillcolor(RGB(0, 255, 1));
        setlinecolor(RGB(255, 255, 255));
        fillrectangle(160, 515, 160 + hp * 4, 540);
        rectangle(160, 515, 160 + hp * 4, 540);
        //返回是否死亡
        return Body::hurt(now, repeat);
    }

protected:
    void del()
    {
        setfillcolor(0);
        setlinecolor(0);
        fillrectangle(x - 25, y - 25, x + 25, y + 25);
        rectangle(x - 25, y - 25, x + 25, y + 25);
        fillrectangle(x, y + 5, x + 40, y - 5);
        rectangle(x, y + 5, x + 40, y - 5);
    }
};

#define BT_MAX 8
class Battle
{
public:
    Boss        bo;//敌人
    Tank        tk;//玩家
    Bullet      bt[BT_MAX];//玩家的子弹
    Bullet      ebt[BT_MAX];//敌人的子弹

    Battle()
    {
        tk.x = 30;
        tk.y = 30;
        bo.x = 580;
        bo.y = 240;
        bo.mDelay = clock();//初始化延时
        bo.mDelayAtt = clock();
        bo.clr = RGB(0, 130, 125);
        tk.clr = RGB(150, 180, 210);
    }

protected:
    // 更新子弹位置
    // btCnt:表的尺寸
    // bt:子弹对象表
    void updateBullet(int btCnt, Bullet bt[], clock_t now)
    {
        //遍历子弹,使子弹刷新
        while (btCnt-->0){
            if (bt[btCnt].on == false) continue;
            // 刷新位置(传递当前时刻,左右边界)
            if (bt[btCnt].update(now, 5, 635)) continue;
        }
    }
    // 扫描检查子弹是否集中物体
    // btCnt:表的尺寸
    // bt:子弹对象表
    // area: 需要击中的方块区域
    int  scanBulletHurt(int btCnt, Bullet bt[], const RECT& area)
    {
        int c = 0;
        //遍历子弹,计算碰撞次数
        while (btCnt-->0){
            if (bt[btCnt].on == false) continue;
            if ((bt[btCnt].x + 5 >= area.left && bt[btCnt].x - 5 <= area.right) &&
                (bt[btCnt].y - 5 < area.bottom && bt[btCnt].y + 5 > area.top)){

                // 击中了
                bt[btCnt].on = false;
                ++c;
            }
        }
        return c;
    }
    // 显示子弹
    // btCnt:表的尺寸
    // bt:子弹对象表
    void showBullet(int btCnt, Bullet bt[], clock_t now)
    {
        //遍历子弹,使子弹刷新
        while (btCnt-->0){
            if (bt[btCnt].on == false) continue;
            // 绘制到新位置
            bt[btCnt].show(now);
        }
    }

public:
    // 返回是否死亡结束(0:没死, 1:自己挂, 2:敌人挂)
    // tkMoveDirection: 坦克移动方向(-1,0,1)=(上,停,下)
    // tkFire:坦克开火
    int update(clock_t now, int tkMoveDirection, int tkFire)
    {
        int ret = 0;
        do{// 这个循环结构是用来跳转的,没有循环意义
            updateBullet(BT_MAX, bt, now);      //自身子弹刷新
            updateBullet(BT_MAX, ebt, now);     //敌人子弹刷新
            tk.update(now, 28, 452, 3*tkMoveDirection);  //自己更新(上下边界和移动速度)
            bo.update(now, 40, 440, 5);         //敌人更新(上下边界和移动速度)
            // 检测敌人区域 计算敌人伤害
            if (int c = scanBulletHurt(BT_MAX, bt, { bo.x - 20, bo.y - 40, bo.x + 20, bo.y + 40 })){
                // 返回是否死亡
                if (bo.hurt(now, c)){ ret = 2; break; }
            }

            // 检测自身区域 计算自身伤害
            if (int c = scanBulletHurt(BT_MAX, ebt, { tk.x - 25, tk.y - 25, tk.x + 25, tk.y + 25 })){
                // 返回是否死亡
                if (tk.hurt(now,c)){ ret = 1; break; }
            }

            // 坦克开火
            if (tkFire && (now - tk.mDelayAtt > 800)){
                for (int i = BT_MAX; i--;){
                    if (bt[i].on) continue;

                    // 发射子弹
                    tk.mDelayAtt = now;
                    tk.fire(now, bt[i]);
                    break;
                }
            }

            // 敌人自动开火
            if (now - bo.mDelayAtt > 700){
                for (int i = BT_MAX; i--;){
                    if (ebt[i].on) continue;

                    // 发射子弹
                    bo.mDelayAtt = now;
                    bo.fire(now, ebt[i]);
                    break;
                }
            }
        } while (0);

        // 显示绘制 敌我子弹
        bo.show(now);
        tk.show(now);
        showBullet(BT_MAX, bt, now);
        showBullet(BT_MAX, ebt, now);
        return ret;
    }
};


int main()
{
    // 初始化easyX 图形窗口(尺寸 640 x 550,    4:NOMINIMIZE 没有最小化功能)
    initgraph(640, 550, 4); 

    settextcolor(RGB(0, 254, 0));   // 设置文本颜色
    settextstyle(35, 0, _T("黑体"));// 设置文本字体
    outtextxy(150, 200, _T("W,S移动,K攻击"));
    Sleep(1000);

    setlinecolor(0); //0: 黑色
    setfillcolor(0);
    rectangle(0, 0, 640, 550);
    fillrectangle(0, 0, 640, 550);
    setlinecolor(RGB(255, 255, 255));
    setfillcolor(RGB(255, 255, 255));

    line(0, 481, 640, 481);//分割画面与血条

    settextstyle(20, 0, _T("黑体"));
    outtextxy(10, 485, _T("BOSS的生命值:"));
    setfillcolor(RGB(230, 0, 1));
    fillrectangle(160, 485, 560, 510);//敌人血条
    outtextxy(10, 520, _T("玩家的生命值:"));
    setfillcolor(RGB(0, 255, 1));
    fillrectangle(160, 515, 560, 540);//玩家血条

    // 启动游戏
    Battle btl;         // 战场控制类对象
    int bGameover = 0;
    DWORD t, t1 = GetTickCount();
    BeginBatchDraw();
    while (true){
        // 退出程序建检测
        if (GetAsyncKeyState('Q') & 0x8000)
            break;
        
        // 提取当前时间
        t = GetTickCount();
        while (t - t1 > 0){ // 时间间隔是否达到50ms
            // 输入
            int tkMove = 0, tkFire = 0;
            if ((GetAsyncKeyState('W') & 0x8000) ||//玩家移动
                (GetAsyncKeyState('w') & 0x8000)){
                tkMove = -1; //左
            }
            else if ((GetAsyncKeyState('S') & 0x8000) ||//玩家移动
                (GetAsyncKeyState('s') & 0x8000)){
                tkMove = 1; //右
            }
            else if ((GetAsyncKeyState('K') & 0x8000) || //玩家开火
                (GetAsyncKeyState('k') & 0x8000)){
                tkFire = 1; //开火
            }

            // 绘制
            //cleardevice();

            // 更新游戏逻辑
            bGameover = btl.update(clock(), tkMove, tkFire);

            if (bGameover == 1){ //玩家死了
                settextcolor(RGB(254, 0, 0));
                settextstyle(35, 0, _T("黑体"));
                outtextxy(140, 200, _T("你被boss打败了!"));
                break;
            }
            else if (bGameover == 2){ //敌人死了
                settextcolor(RGB(0, 254, 0));
                settextstyle(35, 0, _T("黑体"));
                outtextxy(150, 200, _T("你打败了boss!你赢了!!"));
                break;
            }

            // 输出缓冲图案
            FlushBatchDraw();
            t1 = t + 50;
        }

        // 死亡后结束游戏
        if (bGameover) break;
    }

    //结束绘制
    EndBatchDraw();
    Sleep(5000);
    closegraph();
    return 0;
}