#include <graphics.h>
#include <conio.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <string.h>

// 引用 Windows Multimedia API
#pragma comment(lib,"Winmm.lib")

/***********宏定义区******************/
#define  X 500            // 保存球点坐标的个数
#define Y 500

int len = 640;
int wide = 480;
MCI_PLAY_PARMS mciplay;//定义播放参数mciplay
/***********结构定义区****************/
struct food
{
    int x;                        // 横坐标
    int y;                        // 纵坐标
    int flag;                    // 金币状态 1: 有金币; 0:没有金币
}food;

struct snake
{
    int x[X];                    // 球的横坐标
    int y[Y];
    int life;                    // 生命状态
    int node;                // 球的大小
    char direction;        // 球的方向
    int score;                // 分数
    int grade;                // 难度
    int speed;                // 速度
}snake;

/*************函数声明区**************/
void initsnake();                                        // 初始化一个球
void print_wall();                                    // 输出围墙
void out_food();                                    // 出现金币
void Gameover();                                    // 结束游戏
void Gamebegin();                                    // 开始游戏
void dead_case();                                    // 死亡情况(撞墙)
void eat_food();                                        // 吃金币
void init_data();                                        // 初始化数据
void get_direction(char ch);                    // 得到方向
void print_snake();                                    // 输出球
void print_score();                                    // 输出分数
void set_grade();                                    // 设置等级
void RealseMusic(LPCTSTR strResName,char *DeviceStr);                // 播放资源文件中的音乐
void pass();                                            // 通关函数

/****************************************************/
// 提取指定模块中的资源文件
// 参数:
//        strDstFile:        目标文件名。提取的资源将保存在这里;
//        strResType:        资源类型;
//        strResName:        资源名称;
// 返回值:
//        true: 执行成功;
//        false: 执行失败。
/****************************************************/
bool ExtractResource(LPCTSTR strDstFile, LPCTSTR strResType, LPCTSTR strResName);    // 提取资源到临时文件

/************函数定义区***************/
void pass()
{
    if (snake.speed == 20)       
    {
        EndBatchDraw();
        IMAGE img;
        loadimage(&img,"IMAGE","PASS",len,wide,true);     // s输出通关图片
        putimage(0,0,&img);
        _getch();
        exit(0);
    }
}

bool ExtractResource(LPCTSTR strDstFile, LPCTSTR strResType, LPCTSTR strResName)
{
    // 创建文件
    HANDLE hFile = ::CreateFile(strDstFile, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS,   
                                                FILE_ATTRIBUTE_TEMPORARY, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
        return false;

    // 查找资源文件中、加载资源到内存、得到资源大小
    HRSRC    hRes    = ::FindResource(NULL, strResName, strResType);
    HGLOBAL    hMem    = ::LoadResource(NULL, hRes);
    DWORD    dwSize    = ::SizeofResource(NULL, hRes);

    // 写入文件
    DWORD dwWrite = 0;      // 返回写入字节
    ::WriteFile(hFile, hMem, dwSize, &dwWrite, NULL);
    ::CloseHandle(hFile);

    return true;
}

void RealseMusic(LPCTSTR strResName,char *DeviceStr)
{
    // 产生临时文件的文件名
    TCHAR tmpmp3[_MAX_PATH];
    ::GetTempPath(_MAX_PATH, tmpmp3);
    _tcscat_s(tmpmp3,strResName);                        // VC 请用 _tcscat函数

    // 将 MP3 资源提取为临时文件
    ExtractResource(tmpmp3, "MP3",strResName);

    // 打开音乐
    TCHAR mcicmd[300];
    _stprintf_s(mcicmd,"open \"%s\" alias %s",tmpmp3,DeviceStr);
    mciSendString(mcicmd, NULL, 0, NULL);

    // 播放音乐
//    mciSendString("play mymusic", NULL, 0, NULL);
}

void set_grade()
{
    int grade = 0;
   
    if (snake.speed > 30)
    {   
        grade = snake.score / 100 + 1;
        if (snake.grade != grade)
        {
            snake.grade = grade;    // 最高只有7级
            snake.speed -= 30;
        }
    }
}

void print_score()
{
    char score[10] = "0";
    char grade[3] = "0";
    int x = 0, y1 = 0,y2 = 0;
   
    x =( len /3 + len % 3) * 2;
    y1 = wide / 5;
    y2 = wide / 3;
    set_grade();
    _itoa_s(snake.score,score,10);
    _itoa_s(snake.grade,grade,10);

    setbkmode(TRANSPARENT);                                // 设置文字背景透明
    setcolor(RGB(255,255,0));                                    // 设置文字颜色
    outtextxy(x + len / 10,y1,"分数:");
    outtextxy(x + len / 6,y1,score);
    outtextxy(x + len / 10,y2,"操作说明");
    outtextxy(x + len / 10,y2 + 20,"a:向左走");
    outtextxy(x + len / 10,y2 + 40,"d:向右走");
    outtextxy(x + len / 10,y2 + 60,"w:向上走");
    outtextxy(x + len / 10,y2 + 80,"s:向下走");
    outtextxy(x + len / 10,y1 * 4,"等级:");
    outtextxy(x + len / 10 + 40,y1 * 4,grade);
    outtextxy(x + len / 10 + 50,y1 * 4,"级");
}

void print_snake()
{
    int i;

    setfillcolor(BLACK);                        // 设置当前填充色为黑色
    /* 输出球*/
    for (i = 0; i < snake.node; i++)
        fillrectangle(snake.x[i],snake.y[i],snake.x[i] + 10,snake.y[i] + 10);
    for (i = snake.node; i > 0; i--)        // 球体坐标
    {
        snake.x[i] = snake.x[i - 1];
        snake.y[i] = snake.y[i - 1];
    }
}

void get_direction(char ch)
{
    /* 排除反方向 */
    if (snake.direction != 'a' &&  ch == 'd')
        snake.direction = 'd';
    else if (snake.direction != 'd' &&  ch == 'a')
        snake.direction = 'a';
    else if (snake.direction != 'w' &&  ch == 's')
        snake.direction = 's';
    else if (snake.direction != 's' &&  ch == 'w')
        snake.direction = 'w';

    /* 球动过程中的方向 */
    switch (snake.direction)
    {
    case 'a': snake.x[0] -= 10; break;
    case 'd': snake.x[0] += 10;break;                       
    case 'w': snake.y[0] -= 10;break;
    case 's': snake.y[0] += 10;break;
    }
}

void init_data()
{
    /***初始化***/
    snake.score = 0;
    snake.x[0] = 20,snake.y[0] = 30;
    snake.node = 1;
    snake.life = 1;                                    // 1表示活着
    food.flag = 0;
    snake.grade = 1;
    snake.speed = 200;
}

void eat_food()
{
    mciplay.dwFrom = 0;
    /* 吃掉金币 */
    if (snake.x[0] == food.x && snake.y[0] == food.y)
    {
        // 播放吃的音乐
        mciSendString("seek AteMusic to 0",0,0,0);
        mciSendString("play AteMusic",NULL,0,0);
        setfillcolor(RGB(219,49,0));               
        fillrectangle(food.x,food.y,food.x + 10,food.y + 10);            // 擦掉食金币

           
        snake.node++;                            // 球大小加1
        food.flag = 0;                            // 改变金币状态
        snake.score += 10;                    // 改变分数   
    }
}

void dead_case()
{
    int i = 0;
    int x = 0;
    x =( len /3 + len % 3) * 2;

    /*  判断撞到撞墙 */
    for (i = 2; i < snake.node; i++)               
    {
        if (snake.x[i] == snake.x[0] && snake.y[i] == snake.y[0])
        {
            snake.life = 0;
            break;
        }
    }

    /* 判断是否撞墙 */
    if (snake.x[0] > x || snake.x[0] < 10 || snake.y[0] < 10 || snake.y[0] > 470)
        snake.life = 0;
}

void Gamebegin()
{
    IMAGE image;
    MOUSEMSG msg;

    // 初始化
    RealseMusic("BeginMusic.mp3","BeginMusic");
    RealseMusic("AteMusic.mp3","AteMusic");
    // 播放开始的音乐
    mciSendString("play BeginMusic", NULL, 0, NULL);

    loadimage(&image,"IMAGE","StartCover",len,wide,true);        // s输出开始图片
    putimage(0,0,&image);

//    setbkmode(TRANSPARENT);                                // 设置文字背景透明
//    setcolor(RGB(255,255,0));                                    // 设置文字颜色
//    outtextxy(len / 3 + 50,wide * 4 / 5,"按任意键继续");

    while (1)                                                        // 等待鼠标点击
    {
        msg = GetMouseMsg();
        if (msg.uMsg ==WM_LBUTTONDOWN)
            break;
    }
    mciSendString("close BeginMusic",NULL,0,NULL);        // 关闭开始音乐
    setbkcolor(RGB(219,49,0));        // 设置屏幕背景色为褐色
    cleardevice();                   
    print_wall();                            // 输出围墙
}

void Gameover()
{

    cleardevice();                            // 清屏
    IMAGE image;                        // 定义图片对像
    char score[10] = "0";                // 临时存储成绩

    loadimage(&image,"IMAGE","End",len,wide,true);        // 获取图片
    putimage(0,0,&image);                        // 输出图片
    setcolor(RED);
    _itoa_s(snake.score,score,10);                // 转换分数为自符
    outtextxy(270,160,"分数:");                // 输出分数
    outtextxy(320,160,score);
    outtextxy(270,180,"GAME OVER");
}

void out_food()
{
    int x = 0;

    x =( len /3 + len % 3) * 2;

    srand((unsigned)time(NULL));
    if (food.flag == 0)                                // 获取金币坐标
    {
        food.x = rand() % (x - 30) + 10;        // 避免出现在围墙范围
        food.y = rand() % 450 + 10;
        food.flag = 1;
    }
    while (food.x % 10 != 0)                    // 保证金币在格子里面要不吃不到
        food.x++;
    while (food.y % 10 != 0)
        food.y++;
    setfillcolor(RED);
    if (food.flag == 1)                                // 输出金币
        fillrectangle(food.x,food.y,food.x + 10,food.y + 10);
}

/* 画出围墙 */
void print_wall()
{   
    int i =0;
    int x = 0;

    x =( len /3 + len % 3) * 2;
    setfillcolor(RGB(220,115,10));                            // 设置围墙填充色
    for (i = 0; i < wide; i += 10)
    {
        fillrectangle(0,i,10,i + 10);                            // 左边围墙
        fillrectangle(x,i,x + 10,i + 10);                        // 中间围墙
        fillrectangle(len - 10,i,len,i + 10);                // 右边围墙                               
    }
    for (i = 0; i < len; i += 10)
    {
        fillrectangle(i,0,i + 10,10);                            // 上边围墙
        fillrectangle(i,wide - 10,i + 10,wide);            // 下边围墙
    }
}

void snake_game()
{
    char ch = '0';
   
    init_data();
   
    print_score();
    setfillcolor(BLACK);                            // 设置当前填充色为黑色
    /* 开始时显示一个点球 */
    solidrectangle(snake.x[0],snake.y[0],snake.x[0] + 10,snake.y[0] + 10);

    snake.direction = _getch();                // 得到方向

    BeginBatchDraw();
    while (ch != 27)                                    // 按下esc键就退出循环
    {   
        print_wall();
        out_food();                                    // 输出金币

        if (_kbhit())
            ch = _getch();
       
        get_direction(ch);
        print_snake();
        dead_case();        // 球是否撞墙
        /* 判断生命状态 */
        if (snake.life == 0)
            break;   
        eat_food();
        print_score();
        FlushBatchDraw();
        Sleep(snake.speed);
        cleardevice();
        pass();                // 判断通关
    }
    EndBatchDraw();
}

void main()
{
    initgraph(len,wide);

    Gamebegin();
    snake_game();
    Gameover();

    _getch();
    closegraph();
}