C/C++实现植物大战僵尸(PVZ)(打地鼠版)

news2024/12/25 14:01:28

在这里插入图片描述

在这里插入图片描述在这里插入图片描述

🚀欢迎互三👉:程序猿方梓燚 💎💎
🚀关注博主,后期持续更新系列文章
🚀如果有错误感谢请大家批评指出,及时修改
🚀感谢大家点赞👍收藏⭐评论✍

游戏效果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、准备工作

确保你安装了一个 C++ 编译器,如 Visual Studio 2019 等。安装了EasyX图形库。并且了解基本的 C++ 语法,包括变量声明、数据类型、控制结构(如循环和条件语句)等。

二、代码结构概述

整个代码实现了一个类似《植物大战僵尸》的游戏。
代码主要分为以下几个部分:
头文件(head.h):包含了游戏中所需的各种库的引用、宏定义、结构体、枚举类型、类的声明等。
源文件(PvZ.cpp):实现了游戏的各种功能函数和主函数。

三、头文件(head.h)详解

1、库引用和宏定义:

  • #include <iostream>等一系列库的引用为程序提供了输入输出、时间处理、图形绘制、声音播放等功能。

  • 宏定义了各种游戏元素的类型,例如不同的植物和僵尸类型、地图状态等,方便在程序中使用统一的标识符来表示这些元素。

#define GRASS 0
#define GRAVE1 1
//...
#define BUCKETHEADZOMBIE 16

这些宏定义使得在程序中可以使用诸如GRASS表示草地、BUCKETHEADZOMBIE表示铁桶僵尸等,提高了代码的可读性和可维护性。

2、结构体和枚举类型:

  • coordinate结构体定义了坐标,方便在程序中表示游戏元素的位置。
struct coordinate
{
    int x;
    int y;
};
  • CURSORFLAG枚举类型定义了鼠标光标的不同状态,用于确定玩家当前可以进行的操作。
enum CURSORFLAG
{
    Chammer, 
    CpotatoMine,
    Ciceshroom,
    Cgravebuster
};

3、类的声明:

  • Bang类表示爆炸效果,包含爆炸的位置和倒计时等成员变量,用于在游戏中显示爆炸效果并控制其持续时间。
class Bang
{
public:
    int No;
    int x;
    int y;
    int countDown;

    Bang(int x,int y)
    {
        No = bangNum;
        bangNum++;
        this->x = x;
        this->y = y;
        countDown = 20;
    }
};
  • Sun类表示太阳,包含太阳的位置、帧、编号等成员变量,用于在游戏中生成和管理太阳资源。
class Sun
{
public:
    int x;
    int y;
    int frame;
    int No;
    int changeFrameCountDown;
    int goToCount;
    int goToCountFrame;
    int tempX;
    int tempY;

    Sun(int x,int y)
    {
        frame = 0;
        No=sunNum;
        sunNum++;
        this->x = x;
        this->y = y;
        this->tempX = x;
        this->tempY = y;
        changeFrameCountDown = 5;
        goToCount = 0;
        goToCountFrame = 10;
    }
};
  • Plant类是植物的基类,包含植物的类型、生命值、帧编号等成员变量,为不同类型的植物提供了共同的属性和方法。
class Plant
{
public:
    int type;
    int HP;
    int frameNo;
    int No;
    int x;
    int y;
    int changeFrameCountDown;

    Plant()
    {
        No = plantNum;
        plantNum++;
        changeFrameCountDown = 5;
        HP = 6;
    }
    ~Plant(){}
};
  • PotatoMineGraveBusterIceShroom类分别是土豆雷、墓碑吞噬者、寒冰菇的具体类,继承自Plant类并添加了各自特有的成员变量,实现了不同植物的特定功能。
  • Zombie类是僵尸的基类,包含僵尸的生命值、行、位置、状态等成员变量,为不同类型的僵尸提供了共同的属性和方法。
class Zombie
{
public:
    int HP;
    int row;
    int location;
    int emerge1walk2eat3;
    int frameNo;
    int height;
    int No;
    int changeFrameCountDown;
    int isFrozen;
    int isSlowed;
    int type;

    Zombie()
    {
        No = zombieNum;
        zombieNum++;
        isFrozen = 0;
        isSlowed = 0;
        height = 115;
        frameNo = 19;
        emerge1walk2eat3 = 1;
        changeFrameCountDown = 10;
    }
};
  • NormalZombieConeheadZombieBucketheadZombie类分别是普通僵尸、路障僵尸、铁桶僵尸的具体类,继承自Zombie类并设置了不同的生命值和类型,实现了不同僵尸的特定行为。
  • Lawnmower类表示除草机,包含除草机的位置和状态等成员变量,用于在游戏中处理僵尸与除草机的交互。
class Lawnmower
{
public:
    int location = -20;
    int isActivated = 0;
    int isOut = 0;
};
  • NodeLinkList模板类用于实现链表结构,分别表示链表节点和链表,用于管理游戏中的各种对象,如太阳、植物、僵尸等。
template<class T>
class Node
{
public:
    T* content;
    Node* next = NULL;
    Node(T* t)
    {
        content = t;
    }
};

template<class T>
class LinkList
{
public:
    Node<T>* head;  
    Node<T>* tail;

    LinkList()
    {
        head = NULL;
        tail = NULL;
    };

    LinkList(Node<T> node)
    { 
        head = node; 
        tail = node; 
    };

    ~LinkList()
    { 
        DeleteAllNode();
    }     

    void InsertNode(T* t)
    //... (其他函数的实现)
};

四、源文件(PvZ.cpp)详解

1、函数声明和命名空间:

  • 使用各种编译指令和命名空间声明,为程序提供必要的设置和功能。
  • using namespace std;使得可以直接使用标准库中的名称而无需加上std::前缀,方便了代码的编写。

2、初始化函数(init):

void init()
{
    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 9; j++)
        {
            mapState[i][j] = GRASS;
        }
    }

    currentSunshine = 0;
    plants.DeleteAllNode();
    zombies.DeleteAllNode();
    suns.DeleteAllNode();
    bangs.DeleteAllNode();

    for (int i = 0; i < 5; i++)
    {
        lawnmowers[i] = new Lawnmower();
    }

    normalfrequency = 0.002;
    coneheadfrequency = 0.0025;
    bucketheadfrequency = 0.0028;
    SunsFrequency = 0.05;
    isNewGame = 1;
    isHitting = 0;
    hammerRadius = 0;
    drawingHint = 0;
    hintCountDown = 70;
    snowCountDown = 0;
    graveNum = 0;
    Win1Lose2 = 0;
}
  • 这个函数初始化了游戏的各种参数,包括地图状态、阳光数量、各种链表的清空、除草机的状态、生成僵尸和太阳的频率等。它为游戏的开始或重新开始提供了一个干净的状态。

3、 读取存档函数(readArchive)和写入存档函数(writeArchive):

  • readArchive函数从文件中读取游戏存档数据,包括地图状态、各种对象的状态等,并将其加载到游戏中。
void readArchive(char name[])
{
    init();
    char path[] = "./archives/", tmppath[200] = { 0 };
    strcat(strcat(tmppath, path), name);
    FILE* fp = fopen(tmppath, "rb");
    ::fread(&mapState, sizeof(mapState), 1, fp);

    for (int i = 0; i < 5; i++)
    {
        lawnmowers[i] = new Lawnmower();
        ::fread(&lawnmowers[i]->location, sizeof(int), 1, fp);
        ::fread(&lawnmowers[i]->isActivated, sizeof(int), 1, fp);
        ::fread(&lawnmowers[i]->isOut, sizeof(int), 1, fp);
    }

    fread(&currentSunshine, sizeof(int), 1, fp);
    //... (读取其他参数和对象的状态)

    while (fread(&separator,sizeof(int),1,fp))
    {
        Sun* tmpSun = new Sun(0, 0);
        fread(&tmpSun->x, sizeof(int), 1, fp);
        fread(&tmpSun->y, sizeof(int), 1, fp);
        fread(&tmpSun->frame, sizeof(int), 1, fp);
        fread(&tmpSun->No, sizeof(int), 1, fp);
        fread(&tmpSun->changeFrameCountDown, sizeof(int), 1, fp);
        fread(&tmpSun->goToCount, sizeof(int), 1, fp);
        fread(&tmpSun->goToCountFrame, sizeof(int), 1, fp);
        fread(&tmpSun->tempX, sizeof(int), 1, fp);
        fread(&tmpSun->tempY, sizeof(int), 1, fp);
        suns.InsertNode(tmpSun);
    }
    fclose(fp);
}
  • writeArchive函数将游戏当前状态写入文件以保存存档。

4、精确延时函数(HpSleep

void HpSleep(int ms)
{
    static clock_t oldclock = clock();

    oldclock += ms * CLOCKS_PER_SEC / 1000;

    if (clock() > oldclock)
        oldclock = clock();
    else
        while (clock() < oldclock)
            Sleep(1);
}
  • 这个函数通过计算时间差实现精确的延时功能,可以精确到 1ms。它用于控制游戏的帧率和动画效果的播放速度。

5、透明图像绘制函数(transparentImage)和添加冰效果函数(addIce):

  • transparentImage函数用于将一个图像以透明的方式绘制到另一个图像上指定的位置。
void transparentImage(IMAGE* dstimg, int x, int y, IMAGE* srcimg)
{
    HDC dstDC = GetImageHDC(dstimg);
    HDC srcDC = GetImageHDC(srcimg);
    int w = srcimg->getwidth();
    int h = srcimg->getheight();
    BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
    AlphaBlend(dstDC, x, y, w, h, srcDC, 0, 0, w, h, bf);
}
  • addIce函数用于给图像添加冰效果,通过调整图像的颜色值实现。
void addIce(IMAGE* targetImage, IMAGE* srcImage, int addRed = 0, int addGreen = 0, int addBlue = 50)
{
    int srcImgWidth = srcImage->getwidth(), srcImgHeight = srcImage->getheight();
    targetImage->Resize(srcImgWidth, srcImgHeight);
    DWORD* pTargetBuffer = GetImageBuffer(targetImage);
    DWORD* pSrcBuffer = GetImageBuffer(srcImage);
    int allPixel = srcImgHeight * srcImgWidth;

    for (int i = 0; i < allPixel; ++i)
    {
        UCHAR r = (UCHAR)GetRValue(pSrcBuffer[i]);
        UCHAR g = (UCHAR)GetGValue(pSrcBuffer[i]);
        UCHAR b = (UCHAR)GetBValue(pSrcBuffer[i]);
        r = r + addRed;
        r = r > 255? 255 : r;
        g = g + addGreen;
        g = g > 255? 255 : g;
        b = b + addBlue;
        b = b > 255? 255 : b;
        pTargetBuffer[i] = (DWORD)RGBA(r, g, b, pSrcBuffer[i] >> 24);
    }
}

6、绘制游戏元素的函数:

  • paintPlantsAndGraves函数绘制植物和墓碑。根据地图状态,在不同的位置绘制不同类型的植物和墓碑,并更新植物的状态。
void paintPlantsAndGraves()
{
    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 9; j++)
        {
            switch (mapState[i][j])
            {
            case GRASS:
                break;
            case GRAVE1:
            {
                transparentImage(NULL, xys[i][j].x - 5, xys[i][j].y, &grave[0]);
                break;
            }
            //... (处理其他地图状态)
            }
        }
    }
}
  • paintZombies函数绘制僵尸。根据僵尸的状态绘制不同的僵尸图像,并处理僵尸的移动、动画和状态变化。
void paintZombies()
{
    Node<Zombie> *cur = zombies.head, *next = NULL;
    while (cur!= NULL)
    {
        Zombie* zombieptr = cur->content;
        if (zombieptr->emerge1walk2eat3 == 1)
        {
            //... (绘制正在冒出来的僵尸)
        }
        else if (zombieptr->emerge1walk2eat3 == 2)
        {
            //... (绘制正在行走的僵尸)
        }
        else if (zombieptr->emerge1walk2eat3 == 3)
        {
            //... (绘制正在吃植物的僵尸)
        }
        // 判断是否冻住或减速
        if (zombieptr->isFrozen > 0)
        {
            //...
        }
        if (zombieptr->isSlowed > 0)
        {
            //...
        }
        // 如果僵尸走到最左边且此行有除草机
        if (zombieptr->location < -50 && lawnmowers[zombieptr->row]->isOut == 0)
        {
            //...
        }
        // 如果僵尸前面有植物
        //...
        // 如果僵尸前面有除草机
        if (lawnmowers[zombieptr->row]->isOut == 0 && zombieptr->location < lawnmowers[zombieptr->row]->location - 30)
        {
            //...
        }
         cur = cur->next;
    }
}
  • paintSuns函数绘制太阳。更新太阳的帧并处理太阳被收集的情况
void paintSuns()
{
    Node<Sun> *cur = suns.head, *next;
    while (cur!= NULL)
    {
        Sun* sun = cur->content;
        transparentImage(NULL, sun->x, sun->y, &sunPictures[sun->frame]);
        sun->changeFrameCountDown--;
        if (sun->changeFrameCountDown == 0)
        {
            sun->changeFrameCountDown = 5;
            sun->frame++;
            if (sun->frame == 22) sun->frame = 0;
            if (sun->goToCount == 1)
            {
                sun->x = sun->tempX / 10 * sun->goToCountFrame;
                sun->y = sun->tempY / 10 * sun->goToCountFrame;
                sun->goToCountFrame--;
                if (sun->goToCountFrame == 0)
                {
                    next = cur->next;
                    suns.DeleteNode(sun->No);
                    cur = next;
                    currentSunshine += 25;
                    continue;
                }
            }
        }
        cur = cur->next;
    }
}
  • 这个函数遍历太阳链表,绘制每个太阳的图像。通过更新太阳的帧编号和倒计时,实现太阳的动画效果。当太阳被点击 (goToCount为 1)时,太阳会逐渐移动到特定位置,然后被收集,增加阳光数量并从链表中删除。
  • paintBangs函数绘制爆炸效果。根据爆炸的倒计时绘制爆炸图像,并在倒计时结束时删除爆炸对象。
void paintBangs()
{
    Node<Bang>* cur = bangs.head,*pre;
    while (cur!= NULL)
    {
        if (cur->content->countDown > 0)
        {
            cur->content->countDown--;
            transparentImage(NULL, cur->content->x, cur->content->y, &bang);
        }
        pre = cur;
        cur = cur->next; 
        if(pre->content->countDown<=0)
            bangs.DeleteNode(pre->content->No);
    }
}
  • 该函数遍历爆炸效果链表,绘制每个爆炸的图像。随着倒计时的减少,不断更新爆炸的状态。当倒计时为 0 时,从链表中删除对应的爆炸对象。
  • paintCursor函数绘制鼠标光标。根据光标的状态绘制不同的图像。
void paintCursor()
{
    if (cursor == Chammer)
    {
        // 如果没锤,画正常角度锤子
        if (!isHitting)
            transparentImage(NULL, mousemsg.x - 45, mousemsg.y - 45, &hammer[0]);
        else
        {
            // 画旋转锤子
            transparentImage(NULL, mousemsg.x - 45, mousemsg.y - 45, &hammer[hammerRadius]);
            hammerRadius++;
            if (hammerRadius == 13)
            {
                hammerRadius = 0;
                isHitting = 0;
            }
        }
    }
    else if (cursor == CpotatoMine)
        transparentImage(NULL, mousemsg.x - 45, mousemsg.y - 45, &potaotoMinePictures[0]);
    else if (cursor == Ciceshroom)
        transparentImage(NULL, mousemsg.x - 45, mousemsg.y - 45, &iceshroomPictures[0]);
    else
        transparentImage(NULL, mousemsg.x - 45, mousemsg.y - 45, &gravebusterPictures[0]);
}
  • 根据鼠标光标的状态(锤子、土豆雷、寒冰菇、墓碑吞噬者),在鼠标位置绘制相应的图像。如果是锤子状态且正在锤击(isHitting为真),则绘制旋转的锤子图像。

7、随机生成游戏元素的函数:

  • generateSunshine函数在一定概率下生成阳光,并将其添加到游戏中。
void generateSunshine(int x, int y)
{
    // 一定概率产生 3 个阳光
    double p = rand() / (double)RAND_MAX;
    if (p < SunsFrequency)
    {
        Sun* sunshine[3];
        for (int i = 0; i < 3; i++)
        {
            sunshine[i] = new Sun(x + 80 + rand() % 100 - 50, y + 60 + rand() % 50 - 25);
            suns.InsertNode(sunshine[i]);
        }
    }
}
  • 这个函数根据给定的概率生成阳光对象,并将它们添加到太阳链表中。阳光的位置在一定范围内随机生成。
  • randomZombies函数随机生成僵尸。根据概率在地图上随机位置生成不同类型的僵尸,并播放相应的音效。
void randomZombies()
{
    // 随机产生僵尸
    for (int i = 0; i < 5; i++)
    {
        for (int j = 3; j < 9; j++)
        {
            if (1 <= mapState[i][j] && mapState[i][j] <= 8)
            {
                double p = rand() / (double)RAND_MAX;
                if (p < normalfrequency)
                {
                    NormalZombie* normalZombie = new NormalZombie();
                    normalZombie->row = i;
                    normalZombie->location = xys[i][j].x - 75;
                    zombies.InsertNode(normalZombie);
                    mciSendString("play./Music/dirt_rise.mp3 from 0", 0, 0, 0);
                }
                else if (normalfrequency <= p && p < coneheadfrequency)
                {
                    ConeheadZombie* coneheadZombie = new ConeheadZombie();
                    coneheadZombie->row = i;
                    coneheadZombie->location = xys[i][j].x - 75;
                    zombies.InsertNode(coneheadZombie);
                    mciSendString("play./Music/dirt_rise.mp3 from 0", 0, 0, 0);
                }
                else if (coneheadfrequency <= p && p < bucketheadfrequency)
                {
                    BucketheadZombie* bucketheadZombie = new BucketheadZombie();
                    bucketheadZombie->row = i;
                    bucketheadZombie->location = xys[i][j].x - 75;
                    zombies.InsertNode(bucketheadZombie);
                    mciSendString("play./Music/dirt_rise.mp3 from 0", 0, 0, 0);
                }
            }
        }
    }

    // 随机呻吟声
    double p = rand() / (double)RAND_MAX;
    if (p < groanFrequency)
    {
        int px = rand() % 6 + 1;
        switch (px)
        {
            case 1:
                mciSendString("play./Music/groan.mp3 from 0", 0, 0, 0);
                break;
            case 2:
                mciSendString("play./Music/groan2.mp3 from 0", 0, 0, 0);
                break;
            case 3:
                mciSendString("play./Music/groan3.mp3 from 0", 0, 0, 0);
                break;
            case 4:
                mciSendString("play./Music/groan4.mp3 from 0", 0, 0, 0);
                break;
            case 5:
                mciSendString("play./Music/groan5.mp3 from 0", 0, 0, 0);
                break;
            case 6:
                mciSendString("play./Music/groan6.mp3 from 0", 0, 0, 0);
                break;
        }
    }
}
  • 这个函数在游戏中随机生成僵尸。首先,根据给定的概率在地图上的特定位置生成普通僵尸、路障僵尸或铁桶僵尸,并播放相应的音效。然后,再次根据概率随机播放僵尸的呻吟声。
  • randomGraves函数随机生成墓碑。确保游戏中有一定数量的墓碑,并随机分布在地图上。
void randomGraves()
{
    // 随机产生墓碑
    while (graveNum < 6 || graveNum > 13)
    {
        graveNum = 0;
        for (int i = 0; i < 5; i++)
        {
            int num = rand() % 4;
            for (int j = 0; j < num; j++)
            {
                int column = rand() % 6 + 3;
                if (mapState[i][column] == 0)
                {
                    mapState[i][column] = rand() % 8 + 1;
                    graveNum++;
                }
                else j--;
            }
        }
    }
}
  • 该函数用于在游戏开始时随机生成一定数量的墓碑。通过不断调整墓碑的数量,确保游戏中有 6 到 13 个墓碑随机分布在地图上。

8、绘制存档名称函数(paintNames)和绘制提示函数(drawHint):

  • paintNames函数读取存档文件夹中的存档文件,并在游戏界面上绘制存档名称。如果存档过多或没有存档,会显示相应的提示信息。
void paintNames()
{
    // 画出存档名称
    getFiles("./archives");
    RECT rect;
    setbkmode(TRANSPARENT);
    settextcolor(RGB(222, 186, 97));
    if (files.size() > 5)
    {
        settextstyle(20, 0, "华文隶书");
        rect = { 268, 135, 538, 335 };
        drawtext("存档过多,请删除 archives", &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
        rect = { 268, 175, 538, 375 };
        drawtext("文件夹下的存档并重启!", &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
    }
    else if (files.size() == 0)
    {
        settextstyle(40, 0, "华文隶书");
        rect = { 268, 159, 538, 360 };
        drawtext("没有存档!", &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
    }
    else
    {
        int h = 189;
        settextstyle(35, 0, "华文隶书");
        for (int i = 0; i < files.size(); ++i)
        {
            rect = { 268, h, 538, h + 40 };
            drawtext(files[i].c_str(), &rect, DT_CENTER);
            h += 40;
        }
    }
}
  • 这个函数首先获取存档文件夹中的存档文件列表。然后,根据存档数量的不同情况,在游戏界面上绘制相应的提示信息或存档名称。如果存档过多,会提示玩家删除一些存档并重启游戏;如果没有存档,则显示 “没有存档!”;如果有存档,则逐个绘制存档名称。
  • drawHint函数根据游戏状态绘制提示信息,如 “此处不能种植物!” 或 “阳光不足!”。
void drawHint()
{
    if (drawingHint!= 0)
    {
        settextcolor(WHITE);
        settextstyle(40, 0, "隶书");
        if (drawingHint == 1)
        {
            drawtext("此处不能种植物!", &rect, DT_CENTER);
        }
        else if (drawingHint == 2)
            drawtext("阳光不足!", &rect, DT_CENTER);

        hintCountDown--;
        if (hintCountDown == 0)
        {
            hintCountDown = 70;
            drawingHint = 0;
        }
    }
}
  • 该函数根据游戏中的提示状态(drawingHint)绘制相应的提示信息在游戏界面上。如果提示状态为 1,则绘制 “此处不能种植物!”;如果提示状态为 2,则绘制 “阳光不足!”。提示会持续一段时间(由hintCountDown控制),然后消失。

五、完整代码&素材

head.h

#include <iostream>
#include <ctime>
#include <string>
#include <graphics.h>
#include <conio.h>
#include <Windows.h>
#include <io.h>
#include <vector>
#include <stdio.h>
#include <cmath>
#include <mmsystem.h>
using namespace std;

#define GRASS 0
#define GRAVE1 1
#define GRAVE2 2
#define GRAVE3 3
#define GRAVE4 4
#define GRAVE5 5
#define GRAVE6 6
#define GRAVE7 7
#define GRAVE8 8
#define POTATO 9
#define POTATOMINE 10
#define POTATOBOOM 11
#define GRAVEBUSTER_GRAVE1 12
#define GRAVEBUSTER_GRAVE2 13
#define GRAVEBUSTER_GRAVE3 14
#define GRAVEBUSTER_GRAVE4 15
#define GRAVEBUSTER_GRAVE5 16
#define GRAVEBUSTER_GRAVE6 17
#define GRAVEBUSTER_GRAVE7 18
#define GRAVEBUSTER_GRAVE8 19
#define ICESHROOM 20
#define NORMALZOMBIE 21
#define CONEHEADZOMBIE 22
#define BUCKETHEADZOMBIE 16

//非游戏参数
int zombieNum=0;
int plantNum=0;
int sunNum=0;
int bangNum=0;
double groanFrequency = 0.0005;
IMAGE potatoBoom;
IMAGE potato;
IMAGE grave[8];
IMAGE hammer[13];
IMAGE tmpImg;
IMAGE tmpImg2;
IMAGE potaotoMinePictures[8];
IMAGE iceshroomPictures[11];
IMAGE gravebusterPictures[28];
IMAGE sunPictures[22];
IMAGE normalZombieWalkPictures[47];
IMAGE normalZombieEmergePictures[20];
IMAGE normalZombieEatPictures[10];
IMAGE coneheadZombieWalkPictures[47];
IMAGE coneheadZombieEmergePictures[20];
IMAGE coneheadZombieEatPictures[10];
IMAGE bucketheadZombieWalkPictures[47];
IMAGE bucketheadZombieEmergePictures[20];
IMAGE bucketheadZombieEatPictures[10];
IMAGE plantsBar;
IMAGE menu;
IMAGE background;
IMAGE selectID;
IMAGE iceTrap;
IMAGE snow;
IMAGE lawnmower;
IMAGE loseGame;
IMAGE winGame;
IMAGE bang;
ExMessage mousemsg;

struct coordinate
{
	int x;
	int y;
};

enum CURSORFLAG
{
	Chammer, 
	CpotatoMine,
	Ciceshroom,
	Cgravebuster
};

coordinate xys[32][32];
CURSORFLAG cursor;
RECT rect = { 0, 500, 820, 600 };
char sunshineNum[10];
char username[200];
vector<string> files;

class Bang
{
public:
	int No;
	int x;
	int y;
	int countDown;

	Bang(int x,int y)
	{
		No = bangNum;
		bangNum++;
		this->x = x;
		this->y = y;
		countDown = 20;
	}
};

class Sun
{
public:
	int x;
	int y;
	int frame;
	int No;
	int changeFrameCountDown;
	int goToCount;
	int goToCountFrame;
	int tempX;
	int tempY;

	Sun(int x,int y)
	{
		frame = 0;
		No=sunNum;
		sunNum++;
		this->x = x;
		this->y = y;
		this->tempX = x;
		this->tempY = y;
		changeFrameCountDown = 5;
		goToCount = 0;
		goToCountFrame = 10;
	}
};

class Plant
{
public:
	int type;
	int HP;
	int frameNo;
	int No;
	int x;
	int y;
	int changeFrameCountDown;

	Plant()
	{
		No = plantNum;
		plantNum++;
		changeFrameCountDown = 5;
		HP = 6;
	}
	~Plant(){}
};

// 土豆雷
class PotatoMine : public Plant
{
public:
	int underCountDown = 400;
	int boomCountDown = 50;

	PotatoMine()
	{
		frameNo = 0;
		type = POTATOMINE;
	}
};

// 墓碑吞噬者
class GraveBuster : public Plant
{
public:
	GraveBuster()
	{
		frameNo = 1;
		type = GRAVEBUSTER_GRAVE1;
	}
};

// 寒冰菇
class IceShroom : public Plant
{
public:
	int frozenCountDown = 200;
	int slowingCountDown = 1000;

	IceShroom()
	{
		frameNo = 0;
		type = ICESHROOM;
	}
};


class Zombie
{
public:
	int HP;
	int row;
	int location;
	int emerge1walk2eat3;
	int frameNo;
	int height;
	int No;
	int changeFrameCountDown;
	int isFrozen;
	int isSlowed;
	int type;

	Zombie()
	{
		No = zombieNum;
		zombieNum++;
		isFrozen = 0;
		isSlowed = 0;
		height = 115;			// 僵尸图像高度
		frameNo = 19;			// 表示播放到第几帧
		emerge1walk2eat3 = 1;	// 正在冒出来用 1 表示,正在行走用 2 表示,正在吃植物用 3 表示
		changeFrameCountDown = 10;
	}
};
class NormalZombie : public Zombie
{
public:
	NormalZombie()
	{
		HP = 1;
		type = NORMALZOMBIE;
	}
};

class ConeheadZombie : public Zombie
{
public:
	ConeheadZombie()
	{
		HP = 2;
		type = CONEHEADZOMBIE;
	}
};

class BucketheadZombie : public Zombie
{
public:
	BucketheadZombie()
	{
		HP = 3;
		type = BUCKETHEADZOMBIE;
	}
};

class Lawnmower
{
public:
	int location = -20;
	int isActivated = 0;
	int isOut = 0;
};

template<class T>
class Node
{
public:
	T* content;
	Node* next = NULL;
	Node(T* t)
	{
		content = t;
	}
};

template<class T>
class LinkList
{
public:
	Node<T>* head;  
	Node<T>* tail;

	LinkList()
	{
		head = NULL;
		tail = NULL;
	};

	LinkList(Node<T> node)
	{ 
		head = node; 
		tail = node; 
	};

	~LinkList()
	{ 
		DeleteAllNode();
	}     

	void InsertNode(T* t)
	{
		Node<T>* node=new Node<T>(t);
		if (head == NULL)
		{
			head = node;
			tail = node;
		}
		else
		{
			tail->next = node;
			tail = node;
		}
	};

	void DeleteNode(int No)
	{
		Node<T>* cur = head,*pre=NULL;
		while (cur != NULL && cur->content->No != No)
		{
			pre = cur;
			cur = cur->next;
		}

		if (pre == NULL)
		{
			head = cur->next;
		}
		else if (cur == NULL)
		{
			cout << "没有找到符合条件的结点!" << endl;
			return;
		}
		else
		{
			pre->next = cur->next;
		}

		if (cur == tail)
		{
			tail = pre;
		}
		delete cur;
	};

	void DeleteAllNode()
	{
		Node<T>* cur = head,*pre=NULL;
		while (tail != NULL)
		{
			pre = cur;
			cur = cur->next;
			DeleteNode(pre->content->No);
		}
	};
};

PvZ.cpp

#include "head.h"
#pragma warning (disable:4996)
#pragma comment( lib, "MSIMG32.LIB")
#pragma comment( lib, "winmm.lib")

// 游戏参数
int mapState[32][32];		// 地图状态。0:空,1:墓碑,2:地雷(没出土),3:地雷(已出土),4:寒冰菇
int currentSunshine;
LinkList<Sun> suns;
LinkList<Plant> plants;
LinkList<Zombie> zombies;
LinkList<Bang> bangs;
Lawnmower* lawnmowers[5];
double normalfrequency;
double coneheadfrequency;
double bucketheadfrequency;
double SunsFrequency;
int isNewGame;
int isHitting;
int hammerRadius;
int drawingHint;
int hintCountDown;
int snowCountDown;
int graveNum;
int Win1Lose2;

void init()
{
	for (int i = 0; i < 5; i++)
	{
		for (int j = 0; j < 9; j++)
		{
			mapState[i][j] = GRASS;
		}
	}

	currentSunshine = 0;
	plants.DeleteAllNode();
	zombies.DeleteAllNode();
	suns.DeleteAllNode();
	bangs.DeleteAllNode();

	for (int i = 0; i < 5; i++)
	{
		lawnmowers[i] = new Lawnmower();
	}

	normalfrequency = 0.002;
	coneheadfrequency = 0.0025;
	bucketheadfrequency = 0.0028;
	SunsFrequency = 0.05;
	isNewGame = 1;
	isHitting = 0;
	hammerRadius = 0;
	drawingHint = 0;
	hintCountDown = 70;
	snowCountDown = 0;
	graveNum = 0;
	Win1Lose2 = 0;
}

void getFiles(string path)
{
	files.clear();
	//文件句柄  
	intptr_t hFile = 0;
	//文件信息  
	struct _finddata_t fileinfo;
	string p;
	if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
	{
		do
		{
			files.push_back(fileinfo.name);
		} while (_findnext(hFile, &fileinfo) == 0);
		_findclose(hFile);
	}
	files.erase(files.begin());
	files.erase(files.begin());
}

void readArchive(char name[])
{
	init();
	char path[] = "./archives/", tmppath[200] = { 0 };
	strcat(strcat(tmppath, path), name);
	FILE* fp = fopen(tmppath, "rb");
	::fread(&mapState, sizeof(mapState), 1, fp);

	for (int i = 0; i < 5; i++)
	{
		lawnmowers[i] = new Lawnmower();
		::fread(&lawnmowers[i]->location, sizeof(int), 1, fp);
		::fread(&lawnmowers[i]->isActivated, sizeof(int), 1, fp);
		::fread(&lawnmowers[i]->isOut, sizeof(int), 1, fp);
	}

	fread(&currentSunshine,		sizeof(int),	1, fp);
	fread(&normalfrequency,		sizeof(double),	1, fp);
	fread(&coneheadfrequency,	sizeof(double),	1, fp);
	fread(&bucketheadfrequency,	sizeof(double),	1, fp);
	fread(&SunsFrequency,		sizeof(double),	1, fp);
	fread(&isNewGame,			sizeof(int),	1, fp);
	fread(&isHitting,			sizeof(int),	1, fp);
	fread(&hammerRadius,		sizeof(int),	1, fp);
	fread(&drawingHint,			sizeof(int),	1, fp);
	fread(&hintCountDown,		sizeof(int),	1, fp);
	fread(&snowCountDown,		sizeof(int),	1, fp);
	fread(&graveNum,			sizeof(int),	1, fp);
	fread(&Win1Lose2,			sizeof(int),	1, fp);

	int separator;
	while (1)
	{
		fread(&separator, sizeof(int), 1, fp);
		if (separator!=1234567)
		{
			Zombie* tmpZombie = new Zombie();
			fseek(fp, -(int)sizeof(int), SEEK_CUR);
			fread(&tmpZombie->HP,					sizeof(int), 1, fp);
			fread(&tmpZombie->row,					sizeof(int), 1, fp);
			fread(&tmpZombie->location,				sizeof(int), 1, fp);
			fread(&tmpZombie->emerge1walk2eat3,		sizeof(int), 1, fp);
			fread(&tmpZombie->frameNo,				sizeof(int), 1, fp);
			fread(&tmpZombie->height,				sizeof(int), 1, fp);
			fread(&tmpZombie->No,					sizeof(int), 1, fp);
			fread(&tmpZombie->changeFrameCountDown,	sizeof(int), 1, fp);
			fread(&tmpZombie->isFrozen,				sizeof(int), 1, fp);
			fread(&tmpZombie->isSlowed,				sizeof(int), 1, fp);
			fread(&tmpZombie->type,					sizeof(int), 1, fp);
			zombies.InsertNode(tmpZombie);
		}
		else break;
	}

	int tmpPlantType;
	while (1)
	{
		fread(&separator, sizeof(int), 1, fp);
		if (separator!=7654321)
		{
			fseek(fp, -(int)sizeof(int), SEEK_CUR);
			fread(&tmpPlantType, sizeof(int), 1, fp);
			switch (tmpPlantType)
			{
				case POTATOMINE:
				{
					PotatoMine* tmpPotatoMine = new PotatoMine();
					tmpPotatoMine->type = tmpPlantType;
					fread(&tmpPotatoMine->frameNo, sizeof(int), 1, fp);
					fread(&tmpPotatoMine->No, sizeof(int), 1, fp);
					fread(&tmpPotatoMine->x, sizeof(int), 1, fp);
					fread(&tmpPotatoMine->y, sizeof(int), 1, fp);
					fread(&tmpPotatoMine->changeFrameCountDown, sizeof(int), 1, fp);
					fread(&tmpPotatoMine->underCountDown, sizeof(int), 1, fp);
					fread(&tmpPotatoMine->boomCountDown, sizeof(int), 1, fp);
					plants.InsertNode(tmpPotatoMine);
					break;
				}
				case GRAVEBUSTER_GRAVE1:
				{
					GraveBuster* tmpGraveBuster = new GraveBuster();
					tmpGraveBuster->type = tmpPlantType;
					fread(&tmpGraveBuster->frameNo, sizeof(int), 1, fp);
					fread(&tmpGraveBuster->No, sizeof(int), 1, fp);
					fread(&tmpGraveBuster->x, sizeof(int), 1, fp);
					fread(&tmpGraveBuster->y, sizeof(int), 1, fp);
					fread(&tmpGraveBuster->changeFrameCountDown, sizeof(int), 1, fp);
					plants.InsertNode(tmpGraveBuster);
					break;
				}
				case ICESHROOM:
				{
					IceShroom* tmpIceShroom = new IceShroom();
					tmpIceShroom->type = tmpPlantType;
					fread(&tmpIceShroom->frameNo, sizeof(int), 1, fp);
					fread(&tmpIceShroom->No, sizeof(int), 1, fp);
					fread(&tmpIceShroom->x, sizeof(int), 1, fp);
					fread(&tmpIceShroom->y, sizeof(int), 1, fp);
					fread(&tmpIceShroom->changeFrameCountDown, sizeof(int), 1, fp);
					fread(&tmpIceShroom->frozenCountDown, sizeof(int), 1, fp);
					fread(&tmpIceShroom->slowingCountDown, sizeof(int), 1, fp);
					plants.InsertNode(tmpIceShroom);
					break;
				}
			}
		}
		else break;
	}

	while (1)
	{
		fread(&separator, sizeof(int), 1, fp);
		if (separator != 357421)
		{
			Bang* tmpBang = new Bang(0,0);
			fseek(fp, -(int)sizeof(int), SEEK_CUR);
			fread(&tmpBang->No, sizeof(int), 1, fp);
			fread(&tmpBang->x, sizeof(int), 1, fp);
			fread(&tmpBang->y, sizeof(int), 1, fp);
			fread(&tmpBang->countDown, sizeof(int), 1, fp);
			bangs.InsertNode(tmpBang);
		}
		else
			break;
	}

	while (fread(&separator,sizeof(int),1,fp))
	{
		Sun* tmpSun = new Sun(0, 0);
		fread(&tmpSun->x, sizeof(int), 1, fp);
		fread(&tmpSun->y, sizeof(int), 1, fp);
		fread(&tmpSun->frame, sizeof(int), 1, fp);
		fread(&tmpSun->No, sizeof(int), 1, fp);
		fread(&tmpSun->changeFrameCountDown, sizeof(int), 1, fp);
		fread(&tmpSun->goToCount, sizeof(int), 1, fp);
		fread(&tmpSun->goToCountFrame, sizeof(int), 1, fp);
		fread(&tmpSun->tempX, sizeof(int), 1, fp);
		fread(&tmpSun->tempY, sizeof(int), 1, fp);
		suns.InsertNode(tmpSun);
	}
	fclose(fp);
}

void writeArchive(char name[])
{
	char path[] = "./archives/", tmppath[200] = { 0 };
	strcat(strcat(tmppath, path), name);
	FILE* fp = fopen(tmppath, "wb");
	::fwrite(mapState, sizeof(mapState), 1, fp);

	for (int i = 0; i < 5; i++)
	{
		::fwrite(&lawnmowers[i]->location, sizeof(int), 1, fp);
		::fwrite(&lawnmowers[i]->isActivated, sizeof(int), 1, fp);
		::fwrite(&lawnmowers[i]->isOut, sizeof(int), 1, fp);
	}

	::fwrite(&currentSunshine, sizeof(int), 1, fp);
	::fwrite(&normalfrequency, sizeof(double), 1, fp);
	::fwrite(&coneheadfrequency, sizeof(double), 1, fp);
	::fwrite(&bucketheadfrequency, sizeof(double), 1, fp);
	::fwrite(&SunsFrequency, sizeof(double), 1, fp);
	::fwrite(&isNewGame, sizeof(int), 1, fp);
	::fwrite(&isHitting, sizeof(int), 1, fp);
	::fwrite(&hammerRadius, sizeof(int), 1, fp);
	::fwrite(&drawingHint, sizeof(int), 1, fp);
	::fwrite(&hintCountDown, sizeof(int), 1, fp);
	::fwrite(&snowCountDown, sizeof(int), 1, fp);
	::fwrite(&graveNum, sizeof(int), 1, fp);
	::fwrite(&Win1Lose2, sizeof(int), 1, fp);

	Node<Zombie>* curZombie = zombies.head;
	while (curZombie != NULL)
	{
		Zombie* zombie = curZombie->content;
		::fwrite(&zombie->HP, sizeof(int), 1, fp);
		::fwrite(&zombie->row, sizeof(int), 1, fp);
		::fwrite(&zombie->location, sizeof(int), 1, fp);
		::fwrite(&zombie->emerge1walk2eat3, sizeof(int), 1, fp);
		::fwrite(&zombie->frameNo, sizeof(int), 1, fp);
		::fwrite(&zombie->height, sizeof(int), 1, fp);
		::fwrite(&zombie->No, sizeof(int), 1, fp);
		::fwrite(&zombie->changeFrameCountDown, sizeof(int), 1, fp);
		::fwrite(&zombie->isFrozen, sizeof(int), 1, fp);
		::fwrite(&zombie->isSlowed, sizeof(int), 1, fp);
		::fwrite(&zombie->type, sizeof(int), 1, fp);
		curZombie = curZombie->next;
	}

	int separator1 = 1234567;
	::fwrite(&separator1, sizeof(int), 1, fp);
	Node<Plant>* curPlant = plants.head;
	while (curPlant != NULL)
	{
		switch (curPlant->content->type)
		{
			case POTATOMINE:
			{
				PotatoMine* potatoMine = static_cast<PotatoMine*>(curPlant->content);
				::fwrite(&potatoMine->type, sizeof(int), 1, fp);
				::fwrite(&potatoMine->frameNo, sizeof(int), 1, fp);
				::fwrite(&potatoMine->No, sizeof(int), 1, fp);
				::fwrite(&potatoMine->x, sizeof(int), 1, fp);
				::fwrite(&potatoMine->y, sizeof(int), 1, fp);
				::fwrite(&potatoMine->changeFrameCountDown, sizeof(int), 1, fp);
				::fwrite(&potatoMine->underCountDown, sizeof(int), 1, fp);
				::fwrite(&potatoMine->boomCountDown, sizeof(int), 1, fp);
				break;
			}
			case ICESHROOM:
			{
				IceShroom* iceShroom = static_cast<IceShroom*>(curPlant->content);
				::fwrite(&iceShroom->type, sizeof(int), 1, fp);
				::fwrite(&iceShroom->frameNo, sizeof(int), 1, fp);
				::fwrite(&iceShroom->No, sizeof(int), 1, fp);
				::fwrite(&iceShroom->x, sizeof(int), 1, fp);
				::fwrite(&iceShroom->y, sizeof(int), 1, fp);
				::fwrite(&iceShroom->changeFrameCountDown, sizeof(int), 1, fp);
				::fwrite(&iceShroom->frozenCountDown, sizeof(int), 1, fp);
				::fwrite(&iceShroom->slowingCountDown, sizeof(int), 1, fp);
				break;
			}
			case GRAVEBUSTER_GRAVE1:
			{
				GraveBuster* graveBuster = static_cast<GraveBuster*>(curPlant->content);
				::fwrite(&graveBuster->type, sizeof(int), 1, fp);
				::fwrite(&graveBuster->frameNo, sizeof(int), 1, fp);
				::fwrite(&graveBuster->No, sizeof(int), 1, fp);
				::fwrite(&graveBuster->x, sizeof(int), 1, fp);
				::fwrite(&graveBuster->y, sizeof(int), 1, fp);
				::fwrite(&graveBuster->changeFrameCountDown, sizeof(int), 1, fp);
				break;
			}
		}
		curPlant = curPlant->next;
	}

	int separator2 = 7654321;
	::fwrite(&separator2, sizeof(int), 1, fp);
	Node<Bang>* curBang = bangs.head;
	while (curBang!= NULL)
	{
		Bang* bang = curBang->content;
		::fwrite(&bang->No, sizeof(int), 1, fp);
		::fwrite(&bang->x, sizeof(int), 1, fp);
		::fwrite(&bang->y, sizeof(int), 1, fp);
		::fwrite(&bang->countDown, sizeof(int), 1, fp);
		curBang = curBang->next;
	}

	int separator3 = 357421;
	::fwrite(&separator3, sizeof(int), 1, fp);
	Node<Sun>* curSun = suns.head;
	while (curSun != NULL)
	{
		Sun* sun = curSun->content;
		::fwrite(&sun->x, sizeof(int), 1, fp);
		::fwrite(&sun->y, sizeof(int), 1, fp);
		::fwrite(&sun->frame, sizeof(int), 1, fp);
		::fwrite(&sun->No, sizeof(int), 1, fp);
		::fwrite(&sun->changeFrameCountDown, sizeof(int), 1, fp);
		::fwrite(&sun->goToCount, sizeof(int), 1, fp);
		::fwrite(&sun->goToCountFrame, sizeof(int), 1, fp);
		::fwrite(&sun->tempX, sizeof(int), 1, fp);
		::fwrite(&sun->tempY, sizeof(int), 1, fp);
		curSun = curSun->next;
	}

	fclose(fp);
}

// 精确延时函数(可以精确到 1ms,精度 ±1ms)
// by yangw80<yw80@qq.com>, 2011-5-4
void HpSleep(int ms)
{
	static clock_t oldclock = clock();		// 静态变量,记录上一次 tick

	oldclock += ms * CLOCKS_PER_SEC / 1000;	// 更新 tick

	if (clock() > oldclock)					// 如果已经超时,无需延时
		oldclock = clock();
	else
		while (clock() < oldclock)			// 延时
			Sleep(1);						// 释放 CPU 控制权,降低 CPU 占用率
//			Sleep(0);						// 更高精度、更高 CPU 占用率
}

void transparentImage(IMAGE* dstimg, int x, int y, IMAGE* srcimg)
{
	HDC dstDC = GetImageHDC(dstimg);
	HDC srcDC = GetImageHDC(srcimg);
	int w = srcimg->getwidth();
	int h = srcimg->getheight();
	BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
	AlphaBlend(dstDC, x, y, w, h, srcDC, 0, 0, w, h, bf);
}

void paintPlantsAndGraves()
{
	// 画植物和墓碑
	for (int i = 0; i < 5; i++)
	{
		for (int j = 0; j < 9; j++)
		{
			switch (mapState[i][j])
			{
			case GRASS:
				break;
			case GRAVE1:
			{
				transparentImage(NULL, xys[i][j].x - 5, xys[i][j].y, &grave[0]);
				break;
			}
			case GRAVE2:
			{
				transparentImage(NULL, xys[i][j].x - 5, xys[i][j].y, &grave[1]);
				break;
			}
			case GRAVE3:
			{
				transparentImage(NULL, xys[i][j].x - 5, xys[i][j].y, &grave[2]);
				break;
			}
			case GRAVE4:
			{
				transparentImage(NULL, xys[i][j].x - 5, xys[i][j].y, &grave[3]);
				break;
			}
			case GRAVE5:
			{
				transparentImage(NULL, xys[i][j].x - 5, xys[i][j].y, &grave[4]);
				break;
			}
			case GRAVE6:
			{
				transparentImage(NULL, xys[i][j].x - 5, xys[i][j].y, &grave[5]);
				break;
			}
			case GRAVE7:
			{
				transparentImage(NULL, xys[i][j].x - 5, xys[i][j].y, &grave[6]);
				break;
			}
			case GRAVE8:
			{
				transparentImage(NULL, xys[i][j].x - 5, xys[i][j].y, &grave[7]);
				break;
			}
			case POTATO:
			{
				transparentImage(NULL, xys[i][j].x, xys[i][j].y + 37, &potato);
				Node<Plant>* cur = plants.head;
				while (cur != NULL)
				{
					if (cur->content->x == i && cur->content->y == j)
					{
						break;
					}
					else cur = cur->next;
				}
				if (cur != NULL)
				{
					PotatoMine* potato = static_cast<PotatoMine*>(cur->content);
					potato->underCountDown--;
					if (potato->underCountDown == 0)
					{
						mapState[i][j] = POTATOMINE;
						mciSendString("play ./Music/dirt_rise.mp3 from 0 ", 0, 0, 0);
					}
				}
				break;
			}
			case POTATOMINE:
			{
				Node<Plant>* cur = plants.head;
				while (cur != NULL)
				{
					if (cur->content->x == i && cur->content->y == j)break;
					else cur = cur->next;
				}                
				if (cur != NULL)
				{
					transparentImage(NULL, xys[i][j].x, xys[i][j].y + 40, &potaotoMinePictures[cur->content->frameNo]);
					cur->content->changeFrameCountDown--;
					if (cur->content->changeFrameCountDown == 0)
					{
						cur->content->changeFrameCountDown = 20;
						cur->content->frameNo++;
						if (cur->content->frameNo == 7)
						{
							cur->content->frameNo = 0;
						}
					}
				}                   
				break;
			}
			case POTATOBOOM:
			{
				transparentImage(NULL, xys[i][j].x - 25, xys[i][j].y + 20, &potatoBoom);
				Node<Plant>* cur = plants.head;
				while (cur != NULL)
				{
					if (cur->content->x == i && cur->content->y == j)break;
					else cur = cur->next;
				}

				if (cur != NULL)
				{
					PotatoMine* potato = static_cast<PotatoMine*>(cur->content);
					potato->boomCountDown--;
					if (potato->boomCountDown == 0)
					{
						plants.DeleteNode(potato->No);
						mapState[i][j] = GRASS;
					}
				}            

				break;
			}
			case GRAVEBUSTER_GRAVE1:
				transparentImage(NULL, xys[i][j].x-5, xys[i][j].y, &grave[0]);
				goto label;
			case GRAVEBUSTER_GRAVE2:
				transparentImage(NULL, xys[i][j].x - 5, xys[i][j].y, &grave[1]);
				goto label;
			case GRAVEBUSTER_GRAVE3:
				transparentImage(NULL, xys[i][j].x - 5, xys[i][j].y, &grave[2]);
				goto label;
			case GRAVEBUSTER_GRAVE4:
				transparentImage(NULL, xys[i][j].x - 5, xys[i][j].y, &grave[3]);
				goto label;
			case GRAVEBUSTER_GRAVE5:
				transparentImage(NULL, xys[i][j].x - 5, xys[i][j].y, &grave[4]);
				goto label;
			case GRAVEBUSTER_GRAVE6:
				transparentImage(NULL, xys[i][j].x - 5, xys[i][j].y, &grave[5]);
				goto label;
			case GRAVEBUSTER_GRAVE7:
				transparentImage(NULL, xys[i][j].x - 5, xys[i][j].y, &grave[6]);
				goto label;
			case GRAVEBUSTER_GRAVE8:
				transparentImage(NULL, xys[i][j].x - 5, xys[i][j].y, &grave[7]);
				goto label;
			{
			label:
				Node<Plant>* cur = plants.head;
				while (cur != NULL)
				{
					if (cur->content->x == i && cur->content->y == j)break;
					else cur = cur->next;
				}
				if (cur != NULL)
				{
					transparentImage(NULL, xys[i][j].x - 10, xys[i][j].y - 10, &gravebusterPictures[cur->content->frameNo]);
					cur->content->changeFrameCountDown--;
					if (cur->content->changeFrameCountDown == 0)
					{
						cur->content->changeFrameCountDown = 10;
						cur->content->frameNo++;
						if (cur->content->frameNo > 27)
						{
							plants.DeleteNode(cur->content->No);
							mapState[i][j] = GRASS;
							graveNum--;
							if (graveNum == 5)
							{
								SunsFrequency = 0.2;
								normalfrequency = 0.003;
								coneheadfrequency = 0.0035;
								bucketheadfrequency = 0.0038;
							}
							else if (graveNum == 3)
							{
								SunsFrequency = 0.4;
								normalfrequency=0.006;
								coneheadfrequency=0.0065;
								bucketheadfrequency=0.0068;
							}
						}
					}
				}        
				break;
			}
			case ICESHROOM:
			{
				Node<Plant>* cur = plants.head;
				while (cur != NULL)
				{
					if (cur->content->x == i && cur->content->y == j)break;
					else cur = cur->next;
				}
				if (cur != NULL)
				{
					transparentImage(NULL, xys[i][j].x, xys[i][j].y + 15, &iceshroomPictures[cur->content->frameNo]);
					cur->content->changeFrameCountDown--;
					if (cur->content->changeFrameCountDown == 0)
					{
						cur->content->changeFrameCountDown = 8;
						cur->content->frameNo++;
						if (cur->content->frameNo > 10)
						{
							plants.DeleteNode(cur->content->No);
							mciSendString("play ./Music/shoop.mp3 from 0 ", 0, 0, 0);
							mapState[i][j] = 0;
							snowCountDown = 20;
							Node<Zombie>* cur = zombies.head;
							while (cur != NULL)
							{
								cur->content->isFrozen = 200;
								cur = cur->next;
							}
						}
					}
				}              
				break;
			}
			}
		}
	}
}

void addIce(IMAGE* targetImage, IMAGE* srcImage, int addRed = 0, int addGreen = 0, int addBlue = 50)
{
	int srcImgWidth = srcImage->getwidth(), srcImgHeight = srcImage->getheight();
	targetImage->Resize(srcImgWidth, srcImgHeight);
	DWORD* pTargetBuffer = GetImageBuffer(targetImage);
	DWORD* pSrcBuffer = GetImageBuffer(srcImage);
	int allPixel = srcImgHeight * srcImgWidth;

#define RGBA(r, g, b, a) ((b) + (g << 8) + (r << 16) + (a << 24))
	for (int i = 0; i < allPixel; ++i)
	{
		UCHAR r = (UCHAR)GetRValue(pSrcBuffer[i]);
		UCHAR g = (UCHAR)GetGValue(pSrcBuffer[i]);
		UCHAR b = (UCHAR)GetBValue(pSrcBuffer[i]);
		r = r + addRed;
		r = r > 255 ? 255 : r;
		g = g + addGreen;
		g = g > 255 ? 255 : g;
		b = b + addBlue;
		b = b > 255 ? 255 : b;
		pTargetBuffer[i] = (DWORD)RGBA(r, g, b, pSrcBuffer[i] >> 24);
	}
}

void generateSunshine(int x, int y)
{
	// 一定概率产生3个阳光
	double p = rand() / (double)RAND_MAX;
	if (p < SunsFrequency)
	{
		Sun* sunshine[3];
		for (int i = 0; i < 3; i++)
		{
			sunshine[i] = new Sun(x + 80 + rand() % 100 - 50, y + 60 + rand() % 50 - 25);
			suns.InsertNode(sunshine[i]);
		}
	}
}

void paintZombies()
{
	// 画僵尸
	Node<Zombie> *cur = zombies.head, *next = NULL;
	while (cur != NULL)
	{
		Zombie* zombieptr = cur->content;
		if (zombieptr->location < -150)
		{
			cur = cur->next;
			zombies.DeleteNode(zombieptr->No);
			continue;
		}
	   
		if (zombieptr->emerge1walk2eat3 == 1)
		{
			if (zombieptr->type == NORMALZOMBIE)
				tmpImg=normalZombieEmergePictures[zombieptr->frameNo];
			else if (zombieptr->type == CONEHEADZOMBIE)
				tmpImg = coneheadZombieEmergePictures[zombieptr->frameNo];
			else
				tmpImg = bucketheadZombieEmergePictures[zombieptr->frameNo];

			transparentImage(NULL, zombieptr->location, xys[zombieptr->row][0].y - 40, &tmpImg);
			zombieptr->frameNo--;
			if (zombieptr->frameNo == 0)
			{
				zombieptr->emerge1walk2eat3 = 2;
			}
		}
		else if (zombieptr->emerge1walk2eat3 == 2)
		{
			if (zombieptr->type == NORMALZOMBIE)
				tmpImg = normalZombieWalkPictures[zombieptr->frameNo];
			else if (zombieptr->type == CONEHEADZOMBIE)
				tmpImg = coneheadZombieWalkPictures[zombieptr->frameNo];
			else
				tmpImg = bucketheadZombieWalkPictures[zombieptr->frameNo];

			if (zombieptr->isFrozen)
			{
				addIce(&tmpImg2, &tmpImg);
				transparentImage(NULL, zombieptr->location, xys[zombieptr->row][0].y - 40, &tmpImg2);
			}
			else if (zombieptr->isSlowed)
			{
				addIce(&tmpImg2, &tmpImg);
				transparentImage(NULL, zombieptr->location, xys[zombieptr->row][0].y - 40, &tmpImg2);
				zombieptr->changeFrameCountDown -= 1;
				if (zombieptr->changeFrameCountDown <= 0)
				{
					zombieptr->changeFrameCountDown = 6;
					zombieptr->location -= 1; 
					zombieptr->frameNo++;
					if (zombieptr->frameNo > 46)
					{
						zombieptr->frameNo = 0;
					}
				}
			}
			else
			{
				transparentImage(NULL, zombieptr->location, xys[zombieptr->row][0].y - 40, &tmpImg);
				zombieptr->changeFrameCountDown -= 2;
				if (zombieptr->changeFrameCountDown <= 0)
				{
					zombieptr->changeFrameCountDown = 6;
					zombieptr->location -= 2;
					zombieptr->frameNo++;
					if (zombieptr->frameNo > 46)
					{
						zombieptr->frameNo = 0;
					}
				}
			}
		}
		else if (zombieptr->emerge1walk2eat3 == 3)
		{
			/*if (zombieptr->type == NORMALZOMBIE)tmpImg = normalZombieEatPictures[zombieptr->frameNo];
			else if (zombieptr->type == CONEHEADZOMBIE)tmpImg = coneheadZombieEatPictures[zombieptr->frameNo];
			else tmpImg = bucketheadZombieEatPictures[zombieptr->frameNo];
			if (zombieptr->isFrozen) {
				addIce(&tmpImg2, &tmpImg);
				transparentImage(NULL, zombieptr->location, xys[zombieptr->row][0].y - 40, &tmpImg2);
			}
			else if (zombieptr->isSlowed) {
				addIce(&tmpImg2, &tmpImg);
				transparentImage(NULL, zombieptr->location, xys[zombieptr->row][0].y - 40, &tmpImg2);
				zombieptr->changeFrameCountDown -= 1;
				if (zombieptr->changeFrameCountDown <= 0) {
					zombieptr->changeFrameCountDown = 6;
					zombieptr->location -= 1;
					zombieptr->frameNo++;
					if (zombieptr->frameNo > 46) {
						zombieptr->frameNo = 0;
					}
				}
			}
			else {
				transparentImage(NULL, zombieptr->location, xys[zombieptr->row][0].y - 40, &tmpImg);
				zombieptr->changeFrameCountDown -= 2;
				if (zombieptr->changeFrameCountDown <= 0) {
					zombieptr->changeFrameCountDown = 25;
					zombieptr->frameNo++;
				}
			}*/
		}
		// 判断是否冻住
		if (zombieptr->isFrozen > 0)
		{
			zombieptr->isFrozen--;
			if (zombieptr->isFrozen == 0)	zombieptr->isSlowed = 400;
			transparentImage(NULL, zombieptr->location+100, xys[zombieptr->row][0].y+70, &iceTrap);           
		}
		// 判断是否减速
		if (zombieptr->isSlowed > 0)
		{
			zombieptr->isSlowed--;
		}
		// 如果僵尸走到最左边且此行有除草机
		if (zombieptr->location < -50 && lawnmowers[zombieptr->row]->isOut == 0)
		{
			lawnmowers[zombieptr->row]->isActivated = 1;
			mciSendString("play lawnmower from 0", 0, 0, 0);
		}
		 如果僵尸前面有植物
		//Node<Plant>* curPlant = plants.head,*pre;
		//while (curPlant != NULL)
		//{
		//	Plant* plant = curPlant->content;
		//	if (zombieptr->row == plant->x
		//		&& zombieptr->location < xys[plant->x][plant->y].x- 25
		//		&& zombieptr->location > xys[plant->x][plant->y-1].x-25)
		//	{
		//		if (zombieptr->emerge1walk2eat3 == 2)
		//		{
		//			zombieptr->emerge1walk2eat3 = 3;
		//			zombieptr->frameNo = 0;
		//			mciSendString("play ./Music/chomp.mp3 from 0", 0, 0, 0);
		//		}
		//		if (zombieptr->emerge1walk2eat3 == 3 && zombieptr->frameNo > 9)
		//		{
		//			zombieptr->frameNo = 0;
		//			mciSendString("play ./Music/chomp.mp3 from 0", 0, 0, 0);
		//			plant->HP--;
		//			if (plant->HP == 0)
		//			{
		//				pre = curPlant;
		//				curPlant = curPlant->next;
		//				plants.DeleteNode(pre->content->No);
		//				zombieptr->emerge1walk2eat3 = 2;
		//				continue;
		//			}
		//		}
		//	}
		//	curPlant = curPlant->next;
		//}
		// 如果僵尸前面有除草机
		if (lawnmowers[zombieptr->row]->isOut == 0 && zombieptr->location < lawnmowers[zombieptr->row]->location - 30)
		{
			next = cur->next;
			zombies.DeleteNode(zombieptr->No);
			generateSunshine(zombieptr->location, xys[zombieptr->row][0].y);
			cur = next;
			continue;
		}      
		cur = cur->next;
	}
}

void paintSuns()
{
	Node<Sun> *cur = suns.head, *next;
	while (cur != NULL)
	{
		Sun* sun = cur->content;
		transparentImage(NULL, sun->x, sun->y, &sunPictures[sun->frame]);
		sun->changeFrameCountDown--;
		if (sun->changeFrameCountDown == 0)
		{
			sun->changeFrameCountDown = 5;
			sun->frame++;
			if (sun->frame == 22)sun->frame = 0;
			if (sun->goToCount ==1 )
			{
				sun->x = sun->tempX / 10 * sun->goToCountFrame;
				sun->y = sun->tempY / 10 * sun->goToCountFrame;
				sun->goToCountFrame--;
				if (sun->goToCountFrame == 0)
				{
					next = cur->next;
					suns.DeleteNode(sun->No);
					cur = next;
					currentSunshine += 25;
					continue;
				}
			}
		}
		cur = cur->next;
	}
}

void paintBangs()
{
	Node<Bang>* cur = bangs.head,*pre;
	while (cur != NULL)
	{
		if (cur->content->countDown > 0)
		{
			cur->content->countDown--;
			transparentImage(NULL, cur->content->x, cur->content->y, &bang);
		}
		pre = cur;
		cur = cur->next; 
		if(pre->content->countDown<=0)
			bangs.DeleteNode(pre->content->No);
	}
}

void paintCursor()
{
	if (cursor == Chammer)
	{
		// 如果没锤,画正常角度锤子
		if (!isHitting)
			transparentImage(NULL, mousemsg.x - 45, mousemsg.y - 45, &hammer[0]);
		else
		{
			// 画旋转锤子
			transparentImage(NULL, mousemsg.x - 45, mousemsg.y - 45, &hammer[hammerRadius]);
			hammerRadius++;
			if (hammerRadius == 13)
			{
				hammerRadius = 0;
				isHitting = 0;
			}
		}
	}
	else if (cursor == CpotatoMine)
		transparentImage(NULL, mousemsg.x - 45, mousemsg.y - 45, &potaotoMinePictures[0]);
	else if (cursor == Ciceshroom)
		transparentImage(NULL, mousemsg.x - 45, mousemsg.y - 45, &iceshroomPictures[0]);
	else
		transparentImage(NULL, mousemsg.x - 45, mousemsg.y - 45, &gravebusterPictures[0]);
}

void randomZombies()
{
	// 随机产生僵尸
	for (int i = 0; i < 5; i++)
	{
		for (int j = 3; j < 9; j++)
		{
			if (1 <= mapState[i][j] && mapState[i][j] <= 8)
			{
				double p = rand() / (double)RAND_MAX;
				if (p < normalfrequency)
				{
					NormalZombie* normalZombie = new NormalZombie();
					normalZombie->row = i;
					normalZombie->location = xys[i][j].x-75;
					zombies.InsertNode(normalZombie);
					mciSendString("play ./Music/dirt_rise.mp3 from 0", 0, 0, 0);
				}
				else if (normalfrequency <= p && p < coneheadfrequency)
				{
					ConeheadZombie* coneheadZombie = new ConeheadZombie();
					coneheadZombie->row = i;
					coneheadZombie->location = xys[i][j].x-75;
					zombies.InsertNode(coneheadZombie);
					mciSendString("play ./Music/dirt_rise.mp3 from 0", 0, 0, 0);
				}
				else if (coneheadfrequency <= p && p < bucketheadfrequency)
				{
					BucketheadZombie* bucketheadZombie = new BucketheadZombie();
					bucketheadZombie->row = i;
					bucketheadZombie->location = xys[i][j].x-75;
					zombies.InsertNode(bucketheadZombie);
					mciSendString("play ./Music/dirt_rise.mp3 from 0", 0, 0, 0);
				}
			}
		}
	}

	// 随机呻吟声
	double p = rand() / (double)RAND_MAX;
	if (p < groanFrequency)
	{
		int px = rand() % 6 + 1;
		switch (px)
		{
			case 1:
				mciSendString("play ./Music/groan.mp3 from 0", 0, 0, 0);
				break;
			case 2:
				mciSendString("play ./Music/groan2.mp3 from 0", 0, 0, 0);
				break;
			case 3:
				mciSendString("play ./Music/groan3.mp3 from 0", 0, 0, 0);
				break;
			case 4:
				mciSendString("play ./Music/groan4.mp3 from 0", 0, 0, 0);
				break;
			case 5:
				mciSendString("play ./Music/groan5.mp3 from 0", 0, 0, 0);
				break;
			case 6:
				mciSendString("play ./Music/groan6.mp3 from 0", 0, 0, 0);
				break;
		}
	}
}

void paintNames()
{
	// 画出存档名称
	getFiles("./archives");
	RECT rect;
	setbkmode(TRANSPARENT);
	settextcolor(RGB(222, 186, 97));
	if (files.size() > 5)
	{
		settextstyle(20, 0, "华文隶书");
		rect = { 268, 135, 538, 335 };
		drawtext("存档过多,请删除archives", &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
		rect = { 268, 175, 538, 375 };
		drawtext("文件夹下的存档并重启!", &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	}
	else if (files.size() == 0)
	{
		settextstyle(40, 0, "华文隶书");
		rect = { 268, 159, 538, 360 };
		drawtext("没有存档!", &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	}
	else
	{
		int h = 189;
		settextstyle(35, 0, "华文隶书");
		for (int i = 0; i < files.size(); ++i)
		{
			rect = { 268, h, 538, h + 40 };
			drawtext(files[i].c_str(), &rect, DT_CENTER);
			h += 40;
		}
	}   
}

void drawHint()
{
	if (drawingHint !=0)
	{
		settextcolor(WHITE);
		settextstyle(40, 0, "隶书");
		if (drawingHint == 1)
		{
			drawtext("此处不能种植物!", &rect, DT_CENTER);
		}
		else if(drawingHint == 2)
			drawtext("阳光不足!", &rect, DT_CENTER);

		hintCountDown--;
		if (hintCountDown == 0)
		{
			hintCountDown = 70;
			drawingHint = 0;
		}
	}
}

void randomGraves()
{
	// 随机产生墓碑
	while (graveNum < 6 || graveNum>13)
	{
		graveNum = 0;
		for (int i = 0; i < 5; i++)
		{
			int num = rand() % 4;
			for (int j = 0; j < num; j++)
			{
				int column = rand() % 6 + 3;
				if (mapState[i][column] == 0)
				{
					mapState[i][column] = rand() % 8 + 1;
					graveNum++;
				}
				else j--;
			}
		}
	}
}

void beginGame()
{
	// 如果是新游戏
	if (isNewGame)
	{
		// 随机产生墓碑
		randomGraves();
		isNewGame = 0;
		cursor = Chammer;
	}
	mciSendString("open ./Music/Loonboon.mp3 alias BGM2", 0, 0, 0);
	mciSendString("play BGM2 repeat", 0, 0, 0);
	mciSendString("play ./Music/theZombiesareComing.mp3 from 0", 0, 0, 0);
	while (1)
	{
		// 绘图
		cleardevice();
		// 画背景、植物条、菜单、阳光数
		putimage(0, 0, &background);
		putimage(0, 0, &plantsBar);
		transparentImage(NULL, 685, 0, &menu);
		RECT r = { 8, 63, 68, 85 };		// 12, 62, 68, 84
		settextstyle(20, 0, "微软雅黑", 0, 0, FW_BOLD, false, false, false);
		settextcolor(BLACK);
		drawtext(itoa(currentSunshine,sunshineNum,10), &r, DT_CENTER);
		// 画植物和墓碑
		paintPlantsAndGraves();
		// 画僵尸
		paintZombies();
		// 画除草机
		for (int i = 0; i < 5; i++)
		{
			if (lawnmowers[i]->isOut == 0)
			{
				transparentImage(NULL, lawnmowers[i]->location, xys[i][0].y + 45, &lawnmower);
			}
		}

		drawHint();		// 画提示

		paintSuns();		// 画太阳

		paintBangs();		// 画 bang

		paintCursor();		// 画鼠标

		// 画雪花
		if (snowCountDown > 0)
		{
			snowCountDown--;
			transparentImage(NULL, 0, 0, &snow);
		}
		FlushBatchDraw();

		// 计算
		// 除草机状态
		for (int i = 0; i < 5; i++)
		{
			if (lawnmowers[i]->isActivated == 1)
			{
				lawnmowers[i]->location += 5;
				if (lawnmowers[i]->location > 800)
				{
					lawnmowers[i]->isOut = 1;
				}
			}
		}

		// 如果点了鼠标
		while (peekmessage(&mousemsg, EM_MOUSE))
		{           
			if (mousemsg.message == WM_LBUTTONDOWN)
			{
				if (mousemsg.x > 692 && mousemsg.y > 0 && mousemsg.x < 815 && mousemsg.y < 44)
				{
					// 如果点击了菜单,存档退出
					writeArchive(username);
					goto stopGame;
				}
				if (cursor == Chammer)
				{
					// 如果鼠标是锤子
					// 如果点了土豆雷
					if (mousemsg.x > 86 && mousemsg.y > 10 && mousemsg.x < 133 && mousemsg.y < 79)
					{
						if (currentSunshine >= 25)
							cursor = CpotatoMine;
						else
							drawingHint = 2;
					}
					// 如果点了墓碑吞噬者
					else if (mousemsg.x > 145 && mousemsg.y > 10 && mousemsg.x < 191 && mousemsg.y < 79)
					{
						if (currentSunshine >= 75)
							cursor = Cgravebuster;
						else
							drawingHint = 2;
					}
					// 如果点了寒冰菇
					else if (mousemsg.x > 204 && mousemsg.y > 10 && mousemsg.x < 253 && mousemsg.y < 79)
					{
						if (currentSunshine >= 75)
							cursor = Ciceshroom;
						else
							drawingHint = 2;
					}
					else
					{
						hammerRadius = 0;
						isHitting = 1;
						mciSendString("play ./Music/hit.mp3 from 0", 0, 0, 0);
						Node<Zombie>* cur = zombies.head;
						while (cur != NULL)
						{
							Zombie* zombie = cur->content;                        
							if (mousemsg.x > zombie->location + 97 && mousemsg.y > xys[zombie->row][0].y - 40
								&& mousemsg.x < zombie->location + 164 && mousemsg.y < xys[zombie->row][0].y + zombie->height)
							{
								// 如果锤到了僵尸,僵尸减血或死亡 
								bangs.InsertNode(new Bang(mousemsg.x - 70, mousemsg.y - 30));
								zombie->HP--;
								if (zombie->HP == 0)
								{
									zombies.DeleteNode(zombie->No);
									generateSunshine(zombie->location, xys[zombie->row][0].y);
								}
								else if (zombie->HP == 1)
									zombie->type = NORMALZOMBIE;        

								goto skipLittleWhile;
							}
							cur = cur->next;
						}
						Node<Sun>* curSun = suns.head;
						while (curSun != NULL)
						{
							Sun* sun = curSun->content;
							if (mousemsg.x > sun->x + 20 && mousemsg.y > sun->y - 18 && mousemsg.x < sun->x + 108 && mousemsg.y < sun->y + 80)
							{
								// 如果锤中太阳
								curSun->content->goToCount = 1;
								mciSendString("play ./Music/sunshine.mp3 from 0", 0, 0, 0);
								goto skipLittleWhile;
							}
							curSun = curSun->next;
						}
					}
				}
				// 如果鼠标是植物
				else
				{
					int i, j, isInPlantZone = 0;
					for (i = 0; i < 5; i++)
					{
						for (j = 0; j < 9; j++)
						{
							if (mousemsg.x > xys[i][j].x && mousemsg.y > xys[i][j].y
								&& mousemsg.x < xys[i][j].x + 80 && mousemsg.y < xys[i][j].y + 100)
							{
								isInPlantZone = 1;
								break;
							}
						}
						if (isInPlantZone)break;
					}

					if ((cursor != Cgravebuster && mapState[i][j] != GRASS) || isInPlantZone == 0)
					{
						drawingHint = 1;
						continue;
					}

					switch (cursor)
					{
						case CpotatoMine:
						{
							currentSunshine -= 25;
							mapState[i][j] = POTATO;
							PotatoMine* potatoMine = new PotatoMine();
							potatoMine->x = i;
							potatoMine->y = j;
							plants.InsertNode(potatoMine);
							mciSendString("play ./Music/plant.mp3 from 0", 0, 0, 0);
							break;
						}
						case Ciceshroom:
						{
							currentSunshine -= 75;
							mapState[i][j] = ICESHROOM;
							IceShroom* iceshroom = new IceShroom();
							iceshroom->x = i;
							iceshroom->y = j;
							plants.InsertNode(iceshroom);
							mciSendString("play ./Music/plant.mp3 from 0", 0, 0, 0);
							break;
						}
						case Cgravebuster:
						{
							if (mapState[i][j] < 1 || mapState[i][j]>8)
							{
								drawingHint = 1;
								continue;
							}
							currentSunshine -= 75;
							switch (mapState[i][j])
							{
								case GRAVE1:
									mapState[i][j] = GRAVEBUSTER_GRAVE1;
									break;
								case GRAVE2:
									mapState[i][j] = GRAVEBUSTER_GRAVE2;
									break;
								case GRAVE3:
									mapState[i][j] = GRAVEBUSTER_GRAVE3;
									break;
								case GRAVE4:
									mapState[i][j] = GRAVEBUSTER_GRAVE4;
									break;
								case GRAVE5:
									mapState[i][j] = GRAVEBUSTER_GRAVE5;
									break;
								case GRAVE6:
									mapState[i][j] = GRAVEBUSTER_GRAVE6;
									break;
								case GRAVE7:
									mapState[i][j] = GRAVEBUSTER_GRAVE7;
									break;
								case GRAVE8:
									mapState[i][j] = GRAVEBUSTER_GRAVE8;
									break;
								default:
									continue;
									break;
							}
							GraveBuster* gravebuster = new GraveBuster();
							gravebuster->x = i;
							gravebuster->y = j;
							plants.InsertNode(gravebuster);
							mciSendString("play ./Music/gravebusterchomp.mp3 from 0", 0, 0, 0);
							break;
						}
					}
					cursor = Chammer;
				}
			}
			else if(mousemsg.message == WM_RBUTTONDOWN)
			{
				cursor = Chammer;
			}
		}

		//判断土豆雷是否被触发
		for (int i = 0; i < 5; i++)
		{
			for (int j = 0; j < 9; j++)
			{
				if (mapState[i][j] == POTATOMINE)
				{
					Node<Zombie>* curZombie = zombies.head, * pre = NULL;
					while (curZombie != NULL)
					{                       
						pre = curZombie;
						curZombie = curZombie->next;
						if (pre->content->row == i && pre->content->location>xys[i][j].x - 135 && pre->content->location < xys[i][j].x - 20)
						{
							mapState[i][j] = POTATOBOOM;
							mciSendString("play ./Music/potato_mine.mp3 from 0", 0, 0, 0);
							zombies.DeleteNode(pre->content->No);
						}
					}
				}
			}
		}

		//随机产生僵尸
		randomZombies();

		//判断输赢
		if (graveNum == 0 && zombies.head==NULL)
		{
			Win1Lose2 = 1;
			mciSendString("play ./Music/trophy.mp3 from 0", 0, 0, 0);
			goto stopGame;
		}

		Node<Zombie>* cur = zombies.head;
		while (cur != NULL)
		{
			if (cur->content->location < -150)
			{
				Win1Lose2 = 2;
				mciSendString("play ./Music/losemusic.mp3 from 0", 0, 0, 0);
				goto stopGame;
			}
			cur = cur->next;
		}

	skipLittleWhile:
		//延时
		HpSleep(15);
	}
	stopGame:
	mciSendString("close BGM2", 0, 0, 0);
}

void loadImages(IMAGE imgs[], char path[],int n,int begin)
{
	for (int i = 0; i < n; i++)
	{
		char tmpPath[200], frameNo[4];
		strcpy_s(tmpPath, 200, path);
		strcat(strcat(tmpPath, itoa(i + begin, frameNo, 10)), ".png");
		loadimage(&imgs[i], tmpPath);
	}
}

void loading()
{
	loadImages(grave, "./graphics/GraveStones/", 8, 1);
	loadImages(hammer, "./graphics/Screen/hammer/hammer", 13, 1);
	loadImages(sunPictures, "./graphics/Plants/Sun/Sun_", 22, 0);
	loadImages(potaotoMinePictures, "./graphics/Plants/PotatoMine/PotatoMine/PotatoMine_", 8, 0);
	loadImages(iceshroomPictures, "./graphics/Plants/IceShroom/IceShroom/IceShroom_", 11, 0);
	loadImages(gravebusterPictures, "./graphics/Plants/GraveBuster/GraveBuster-", 28, 1);
	loadImages(normalZombieWalkPictures, "./graphics/Zombies/NormalZombie/Zombie/Zombie-", 47, 1);
	loadImages(coneheadZombieWalkPictures, "./graphics/Zombies/ConeheadZombie/ConeheadZombie/ConeheadZombie-", 47, 1);
	loadImages(bucketheadZombieWalkPictures, "./graphics/Zombies/BucketheadZombie/BucketheadZombie/BucketheadZombie-", 47, 1);
	loadImages(normalZombieEmergePictures, "./graphics/Zombies/NormalZombie/ZombieEmerge/Zombie-", 20, 1);
	loadImages(coneheadZombieEmergePictures, "./graphics/Zombies/ConeheadZombie/ConeheadZombieEmerge/Zombie-", 20, 1);
	loadImages(bucketheadZombieEmergePictures, "./graphics/Zombies/BucketheadZombie/BucketheadZombieEmerge/Zombie-", 20, 1);
	loadImages(normalZombieEatPictures, "./graphics/Zombies/NormalZombie/ZombieAttack/ZombieAttack_", 10, 0);
	loadImages(coneheadZombieEatPictures, "./graphics/Zombies/ConeheadZombie/ConeheadZombieAttack/ConeheadZombieAttack_", 10, 0);
	loadImages(bucketheadZombieEatPictures, "./graphics/Zombies/BucketheadZombie/BucketheadZombieAttack/BucketheadZombieAttack_", 10, 0);

	loadimage(&potatoBoom, "./graphics/Plants/PotatoMine/PotatoMineExplode/PotatoMineExplode_0.png");
	loadimage(&potato, "./graphics/Plants/PotatoMine/PotatoMineInit/PotatoMineInit_0.png");
	loadimage(&plantsBar, "./graphics/Screen/ChooserBackground.png");
	loadimage(&background, "./graphics/Screen/Background.jpg");
	loadimage(&selectID, "./graphics/Screen/selectID.png");
	loadimage(&iceTrap, "./graphics/Plants/IceShroom/IceShroomTrap_0.png");
	loadimage(&snow, "./graphics/Plants/IceShroom/IceShroomSnow_0.png");
	loadimage(&menu, "./graphics/Screen/menu.png");
	loadimage(&lawnmower, "./graphics/Screen/lawnmower.png");
	loadimage(&loseGame, "./graphics/Screen/lose.png");
	loadimage(&winGame, "./graphics/Screen/win.png");
	loadimage(&bang, "./graphics/Screen/bang.png");

	mciSendString("open ./Music/chomp.mp3", 0, 0, 0);
	mciSendString("open ./Music/dirt_rise.mp3", 0, 0, 0);
	mciSendString("open ./Music/gravebusterchomp.mp3", 0, 0, 0);
	mciSendString("open ./Music/groan.mp3", 0, 0, 0);
	mciSendString("open ./Music/groan2.mp3", 0, 0, 0);
	mciSendString("open ./Music/groan3.mp3", 0, 0, 0);
	mciSendString("open ./Music/groan4.mp3", 0, 0, 0);
	mciSendString("open ./Music/groan5.mp3", 0, 0, 0);
	mciSendString("open ./Music/groan6.mp3", 0, 0, 0);
	mciSendString("open ./Music/hit.mp3", 0, 0, 0);
	mciSendString("open ./Music/lawnmower.mp3 alias lawnmower", 0, 0, 0);
	mciSendString("open ./Music/losemusic.mp3", 0, 0, 0);
	mciSendString("open ./Music/plant.mp3", 0, 0, 0);
	mciSendString("open ./Music/potato_mine.mp3", 0, 0, 0);
	mciSendString("open ./Music/shoop.mp3", 0, 0, 0);
	mciSendString("open ./Music/theZombiesareComing.mp3", 0, 0, 0);
	mciSendString("open ./Music/trophy.mp3", 0, 0, 0);
	mciSendString("open ./Music/sunshine.mp3", 0, 0, 0);
}

int main()
{
	srand((unsigned)time(NULL));
	loading();    

	for (int i = 0; i < 5; i++)
	{
		for (int j = 0; j < 9; j++)
		{
			xys[i][j].x = 40 + j * 82;
			xys[i][j].y = 70 + i * 100;
			//cout << "xys[" << i << "][" << j << "]:" << xys[i][j].x << "," << xys[i][j].y << endl;;
		}
	}
	
	initgraph(820, 600);
	BeginBatchDraw();
	
	labelBGM:
	mciSendString("open ./Music/Cerebrawl.mp3 alias BGM1", 0, 0, 0);
	mciSendString("play BGM1 repeat", 0, 0, 0);

	label2:
	cleardevice();

	//背景
	putimage(0, 0, &background);

	//对话框
	if (Win1Lose2 == 0)
	{
		transparentImage(NULL, 177, 35, &selectID);
		paintNames();
	}
	else if (Win1Lose2 == 1)
	{
		transparentImage(NULL, 230, 140, &winGame);
	}
	else if (Win1Lose2 == 2)
	{
		transparentImage(NULL, 230, 140, &loseGame);
	}

	FlushBatchDraw();
	while (1)
	{
		getmessage(&mousemsg, EM_MOUSE);
		if (mousemsg.message == WM_LBUTTONDOWN)
		{
			cout << mousemsg.x << "," << mousemsg.y << endl;
			if (Win1Lose2 == 0)
			{
				if (mousemsg.x > 236 && mousemsg.y > 436 && mousemsg.x < 391 && mousemsg.y < 474)
				{
					//点击了“没有我的名字”
					char s[10];
					InputBox(s, 10, "请输入你的姓名:");
					init();
					writeArchive(s);
					goto label2;
				}
				else if (mousemsg.x > 410 && mousemsg.y > 438 && mousemsg.x < 566 && mousemsg.y < 473)
				{
					//点击了退出,存档
					if (strcmp(username, "") != 0)
						writeArchive(username);
					return 0;
				}
				else if (mousemsg.x > 268 && mousemsg.y > 190 && mousemsg.x < 538 && mousemsg.y < 385)
				{
					//点击了存档位置
					if (190 <= mousemsg.y && mousemsg.y < 229)
					{
						if (0 < files.size() && files.size() < 6)
							strcpy(username, (char*)files[0].c_str());
						else continue;
					}
					else if (229 <= mousemsg.y && mousemsg.y < 268)
					{
						if (1 < files.size() && files.size() < 6)
							strcpy(username, (char*)files[1].c_str());
						else continue;
					}
					else if (268 <= mousemsg.y && mousemsg.y < 307)
					{
						if (2 < files.size() && files.size() < 6)
							strcpy(username, (char*)files[2].c_str());
						else continue;
					}
					else if (307 <= mousemsg.y && mousemsg.y < 346)
					{
						if (3 < files.size() && files.size() < 6)
							strcpy(username, (char*)files[3].c_str());
						else continue;
					}
					else if (346 <= mousemsg.y && mousemsg.y < 385)
					{
						if (4 < files.size() && files.size() < 6)
							strcpy(username, (char*)files[4].c_str());
						else continue;
					}
					readArchive(username);
					mciSendString("close BGM1", 0, 0, 0);
					beginGame();
					if (Win1Lose2 == 0)
						goto labelBGM;
					else goto label2;
				}
			}
			else
			{
				if (mousemsg.x > 297 && mousemsg.y > 331 && mousemsg.x < 500 && mousemsg.y < 369)
				{
					init();
					mciSendString("close BGM1", 0, 0, 0);
					beginGame();
					if (Win1Lose2 == 0)
						goto labelBGM;
					else goto label2;
				}
			}
		}
	}
	
	getch();
	EndBatchDraw();
	closegraph();

	return 0;
}

素材

邮箱地址在评论区里发给我,我发给你。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2141324.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Django_Vue3_ElementUI_Release_003_前端Vue3项目初始化

1. 概念扫盲 Node.js是基于ChromeV8引擎&#xff0c;让JS在服务端运行的开发平台&#xff0c;就是JS的一种解释器WebPack就是模块打包机&#xff0c;把浏览器不能直接运行的拓展语言找到并打包为合适的格式给浏览器直接使用Vue基于WebPack构件项目的&#xff0c;并带有合理默认…

MoCo对比损失

MoCo&#xff08;Momentum Contrast&#xff0c;动量对比学习&#xff09;是一种自监督学习方法&#xff0c;由Facebook AI Research提出&#xff0c;主要用于无监督学习视觉表示。在MoCo中&#xff0c;对比损失&#xff08;Contrastive Loss&#xff09;扮演着至关重要的角色&…

在麒麟操作系统中查看进程运行时间

在麒麟操作系统中查看进程运行时间 1、使用ps命令查看进程运行时间1.1 基本命令结构1.2 示例&#xff1a;查看sshd进程的运行时间 2、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Linux操作系统中&#xff0c;包括麒麟&#xff08…

使用Mockito进行单元测试

1、单元测试介绍 Mockito和Junit是用于单元测试的常用框架。单元测试即&#xff1a;从最小的可测试单元&#xff08;如函数、方法或类&#xff09;开始&#xff0c;确保每个单元都能按预期工作。单元测试是白盒测试的核心部分&#xff0c;它有助于发现单元内部的错误。 单元测试…

【Hot100】LeetCode—84. 柱状图中最大的矩形

目录 1- 思路题目识别单调栈 2- 实现⭐84. 柱状图中最大的矩形——题解思路 3- ACM 实现 原题链接&#xff1a;84. 柱状图中最大的矩形 1- 思路 题目识别 识别1 &#xff1a;给定一个数组 heights &#xff0c;求解柱状图的最大面积 单调栈 使用 Stack 来实现&#xff0c;遍…

服务器上PFC配置丢失问题排查与解决方案

现象 基于nccl的多轨通信算力中心出现交换机端口出入方向丢包 分析 机间通信使用RoCE网络&#xff0c;为了避免因丢包导致大量重传报文影响训练性能&#xff0c;我们基于PFC和ECN在交换机和服务器配置搭建了无损网络&#xff0c;理论上是不允许丢包的&#xff0c;现在出现交…

时序差分法

一、时序差分法 时序差分是一种用来估计一个策略的价值函数的方法&#xff0c;它结合了蒙特卡洛和动态规划算法的思想。时序差分方法和蒙特卡洛的相似之处在于可以从样本数据中学习&#xff0c;不需要事先知道环境&#xff1b;和动态 规划的相似之处在于根据贝尔曼方程的思想&…

接口测试(十二)

一、前台、后台、数据库三者关系 fiddler抓包是抓取客户端 --> 服务端 发送的的请求接口 开N个网页&#xff0c;只要有对后端发送请求&#xff0c; fiddler是无差别抓取 F12只抓取当前页面的数据 二、接口概念 接口是什么&#xff1f;— 传递数据的通道 测试系统组件间接口…

CC2530实现按键控制LED

实现按钮控制LED1开启和关闭 1配置环境 2扩展资料 通用io和外设io 设置输入输出 设置输入模式 3实例代码 #include "ioCC2530.h"void delay(int n){int i,j;for(i0;i<n;i){for(j0;j<240;j){asm("NOP");asm("NOP");asm("NOP")…

改编pikachu的打靶经历(题目不全)

前言 题目很少&#xff0c;只做了一些。正常版本的&#xff0c;完整的pikachu可参考下面这个师傅写的 https://www.cnblogs.com/henry666/p/16947270.html xss &#xff08;get&#xff09;反射xss 先尝试 1 这里有长度限制&#xff0c;而且&#xff0c;我改了长度&#xf…

带通滤波反相衰减器电路

1 简介 该可调带通衰减器可在 10Hz 到 100kHz 的频率范围内将信号电平降低 40dB。它还支持独立控制直流输出电平。该设计选择的极点频率在通带之外&#xff0c;以最大限度地减小指定带宽范围内的衰减。 2 设计目标 2.1 输入 2.2 输出 ​​​ 2.3 电源 3 电路设计 根据设计…

TalkSphere项目介绍

TalkSphere项目介绍 文章目录 TalkSphere项目介绍一、前言二、技术栈及开发环境三、主要功能&#xff08;一&#xff09;用户登录与注册&#xff08;二&#xff09;用户历史消息展示&#xff08;三&#xff09;在线用户实时聊天 四、结语 一、前言 在线聊天室作为一个虚拟社交…

JS高级(三)、深浅拷贝,异常处理,this指向总结,改变this指向;节流和防抖

文章目录 一、深浅拷贝1. 浅拷贝&#xff1a;object.assign;解构赋值2. 深拷贝&#xff1a;递归函数、lodash的cloneDeep、JSON 二、异常处理1. throw2. try catch finally 三. this总结1、this的指向2、箭头函数this的指向3、改变函数this的指向 四. 节流和防抖1. 防抖(deboun…

【KiCAD安装教程】

【KiCAD安装教程】 KiCAD安装教程1. 访问KiCAD官网2. 选择版本3. 下载镜像4. 运行安装程序5. 开始安装6. 用户类型选择7. 组件选择8. 安装位置9. 安装过程10. 完成安装 KiCAD安装教程 KiCAD是一款开源的电子设计自动化&#xff08;EDA&#xff09;软件套件&#xff0c;主要用于…

Axure RP 9最新安装程序及汉化包下载(支持Win、Mac版,附下载安装教程)

数月前Axure RP官方已经发布了Axure RP 9的消息&#xff0c;并计划在今年夏天发布beta版本。新版Axure RP 9将是该工具向前迈出的重要一步&#xff0c;其中包括一系列广泛的改进&#xff1a;全面的UI修改&#xff0c;新的设计和文档功能以及前所未有的内部优化。我们已经彻底重…

【渗透测试】——Upload靶场实战(1-5关)

&#x1f4d6; 前言&#xff1a;upload-labs是一个使用 php 语言编写的&#xff0c;专门收集渗透测试和CTF中遇到的各种上传漏洞的靶场。旨在帮助大家对上传漏洞有一个全面的了解。目前一共21关&#xff0c;每一关都包含着不同上传方式。 目录 &#x1f552; 0. 安装phpstudy&a…

Modelsim SE-64 2020.4关闭优化

一、问题起源 本人由于之前一直使用AMD的板子&#xff0c;使用vivado自带仿真器进行功能仿真&#xff0c;由于自带的页面简洁和仿真时间自己还都可以接受就没有什么modelsim联合仿真&#xff0c;又因准备FPGA大赛的国产FPGA易灵思的题目&#xff0c;使用Efinity&#xff0b;Mod…

嵌入式开发与应用实验四——通过串口通信实现收发功能

一、实验目的 1. 了解 USART 的基本特性&#xff1b; 2. 掌握STM32串口通信的基本原理&#xff0c;了解异步通信的概念&#xff1b; 3. 掌握用库函数操作 USART 的方法&#xff0c;学习编程实现STM32的USART通信&#xff1b; 4. 掌握如何使用 STM32 的串口发送和接收数据。…

vue part 11

vuex的模块化与namespace 115_尚硅谷Vue技术_vuex模块化namespace_1_哔哩哔哩_bilibili 116_尚硅谷Vue技术_vuex模块化namespace_2_哔哩哔哩_bilibili vue-router路由 很常见的很重要的应用&#xff1a;Ajax请求&#xff0c;将响应的数据替换掉原先的代码从而实现不跳转页面…

监控系列之-Grafana面板展示及制作

一 Grafana设置添加数据源 1、设置Grafana中文显示 最后保存退出&#xff0c;数据源添加完毕 2、导入node_exporter主机监控面板 此处 有外网的情况下&#xff0c;直接输入对应面板的ID号&#xff0c;然后点击加载即可&#xff1b;无无外网的话&#xff0c;则考虑使用上传仪表…