C++游戏开发——坦克大战

 

引言:

就是练C++而已的。

 代码:

工具类

lag.h

#pragma once#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#pragma comment(lib,"winmm.lib")        // 播放音频using namespace std;/*
功能:窗口居中
参数:hWnd-窗口句柄
*/
void myCenterWindow(HWND hWnd);/*
功能:得到某范围内的随机整数
参数:min - 最小值max - 最大值
返回:某范围内的随机整数
*/
int myRandom(int min, int max);/*
功能:定时器
参数:interval - 延时间隔(毫秒)id - 编号 (0-max)
返回:true-已到时,false-未到时
*/
bool myTimer(int interval, int id);/*
功能:播放音频
参数:fileName - 音频文件repeat - 是否循环播放(true-循环,false-不循环)
返回:无
*/
void myPlayAudio(const string& fileName, bool repeat = false);/*
功能:判断某键是否被按下
参数:keyCode - 键值
返回:true-按下,false-未按下
*/
bool myKeyDown(int keyCode);/*
功能:判断两个矩形是否相交
参数:x1 - 第一个矩形的左上角X坐标y1 - 第一个矩形的左上角Y坐标w1 - 第一个矩形的宽度h1 - 第一个矩形的高度x2 - 第二个矩形的左上角X坐标y2 - 第二个矩形的左上角Y坐标w2 - 第二个矩形的宽度h2 - 第二个矩形的高度
返回:true-相交,false-不相交
*/
bool myRectIntersectRect(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2);/*
功能:绘制透明图片
参数:dstX - 目的X坐标dstY - 目的Y坐标pSrcImg - 源图像指针winWidth - 窗口宽度(判断图片位置是否超出窗口范围,不录则默认为0,表示该参数不用)winHeight - 窗口高度(判断图片位置是否超出窗口范围,不录则默认为0,表示该参数不用)
返回:无
*/
void myDrawImage(int dstX, int dstY, IMAGE* pSrcImg, int winWidth = 0, int winHeight = 0);/*
功能:绘制字符串
参数:text - 字符串内容x - X坐标y - Y坐标
返回:无
*/
void myDrawString(int x, int y, const string& text);

lag.cpp

#include "lag.h"/*
功能:窗口居中
参数:hWnd-窗口句柄
*/
void myCenterWindow(HWND hWnd)
{int screenWidth;		// 屏幕宽度int screenHeight;		// 屏幕高度RECT rect;				// 矩形// 得到屏幕尺寸screenWidth = GetSystemMetrics(SM_CXSCREEN);screenHeight = GetSystemMetrics(SM_CYSCREEN);// 得到窗口尺寸GetWindowRect(hWnd, &rect);// 重新设置窗口尺寸rect.left = (screenWidth - (rect.right - rect.left)) / 2;rect.top = (screenHeight - (rect.bottom - rect.top)) / 3;// 移动窗口到屏幕中间SetWindowPos(hWnd, HWND_TOP, rect.left, rect.top, rect.right, rect.bottom, SWP_NOSIZE);
}/*
功能:得到某范围内的随机整数
参数:min - 最小值max - 最大值
返回:某范围内的随机整数
备注:公式:min + rand() % (max - min + 1)原理:rand() % m => 返回[0-m)的随机数
*/
int myRandom(int min, int max)
{// 生成随机种子(必须在原有毫秒时间的基础上再加随机值,否则在类似for循环中会出现同一种子值的现象)srand((unsigned int)time(NULL) + (unsigned)rand());return (int)(min + rand() % (max - min + 1));
}/*
功能:定时器
参数:interval - 延时间隔(毫秒)id - 编号 (0-max)
返回:true-已到时,false-未到时
*/
bool myTimer(int interval, int id)
{// 静态vector数组(默认数组元素为5,值为0)static vector vectorLastTimeForMyTimer(5, 0);// 根据编号自动扩容if (id < 0) { id = 0; }if ((int)vectorLastTimeForMyTimer.size() <= id) { vectorLastTimeForMyTimer.resize(id + 1, 0); }// 得到当前毫秒并判断是否到时unsigned long long currentTime = GetTickCount64();if (vectorLastTimeForMyTimer[id] == 0)		// 未初始化呢{vectorLastTimeForMyTimer[id] = currentTime;return false;}else{int addTime = (int)(currentTime - vectorLastTimeForMyTimer[id]);if (addTime >= interval){vectorLastTimeForMyTimer[id] = currentTime;return true;}}return false;
}/*
功能:播放音频
参数:fileName - 音频文件repeat - 是否循环播放(true-循环,false-不循环)
返回:无
*/
void myPlayAudio(const string& fileName, bool repeat)
{string command = "play " + fileName;if (repeat) { command += " repeat"; }mciSendString(command.c_str(), 0, 0, 0);
}/*
功能:判断某键是否被按下
参数:keyCode - 键值
返回:true-按下,false-未按下
*/
bool myKeyDown(int keyCode)
{return (GetAsyncKeyState(keyCode) & 0x8000);
}/*
功能:判断两个矩形是否相交
参数:x1 - 第一个矩形的左上角X坐标y1 - 第一个矩形的左上角Y坐标w1 - 第一个矩形的宽度h1 - 第一个矩形的高度x2 - 第二个矩形的左上角X坐标y2 - 第二个矩形的左上角Y坐标w2 - 第二个矩形的宽度h2 - 第二个矩形的高度
返回:true-相交,false-不相交
*/
bool myRectIntersectRect(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2)
{if (x1 > x2 + w2) { return false; }if (x2 > x1 + w1) { return false; }if (y1 > y2 + h2) { return false; }if (y2 > y1 + h1) { return false; }return true;
}/*
功能:载入图片并过滤透明部分(做位运算)
参数:picture_x - 载入图片X坐标picture_y - 载入图片Y坐标picture - 载入图片
返回:无
*/
void myDrawImageAlpha(int  picture_x, int picture_y, IMAGE* picture)
{DWORD* dst = GetImageBuffer();						// GetImageBuffer()函数,用于获取绘图设备的显存指针,EASYX自带DWORD* draw = GetImageBuffer();DWORD* src = GetImageBuffer(picture);				// 获取picture的显存指针int picture_width = picture->getwidth();			// 获取picture的宽度,EASYX自带int picture_height = picture->getheight();			// 获取picture的高度,EASYX自带int graphWidth = getwidth();						// 获取绘图区的宽度,EASYX自带int graphHeight = getheight();						// 获取绘图区的高度,EASYX自带int dstX = 0;										// 在显存里像素的角标// 实现透明贴图 公式: Cp=αp*FP+(1-αp)*BP , 贝叶斯定理来进行点颜色的概率计算for (int iy = 0; iy < picture_height; iy++){for (int ix = 0; ix < picture_width; ix++){int srcX = ix + iy * picture_width;			// 在显存里像素的角标int sa = ((src[srcX] & 0xff000000) >> 24);	// 0xAArrggbb;AA是透明度int sr = ((src[srcX] & 0xff0000) >> 16);	// 获取RGB里的Rint sg = ((src[srcX] & 0xff00) >> 8);		// Gint sb = src[srcX] & 0xff;					// Bif (ix >= 0 && ix <= graphWidth && iy >= 0 && iy <= graphHeight && dstX <= graphWidth * graphHeight){dstX = (ix + picture_x) + (iy + picture_y) * graphWidth;		// 在显存里像素的角标int dr = ((dst[dstX] & 0xff0000) >> 16);int dg = ((dst[dstX] & 0xff00) >> 8);int db = dst[dstX] & 0xff;draw[dstX] = ((sr * sa / 255 + dr * (255 - sa) / 255) << 16)	// 公式: Cp=αp*FP+(1-αp)*BP  ; αp=sa/255 , FP=sr , BP=dr| ((sg * sa / 255 + dg * (255 - sa) / 255) << 8)			// αp=sa/255 , FP=sg , BP=dg| (sb * sa / 255 + db * (255 - sa) / 255);					// αp=sa/255 , FP=sb , BP=db}}}
}/*
功能:绘制透明图片
参数:dstX - 目的X坐标dstY - 目的Y坐标pSrcImg - 源图像指针winWidth - 窗口宽度(判断图片位置是否超出窗口范围,不录则默认为0,表示该参数不用)winHeight - 窗口高度(判断图片位置是否超出窗口范围,不录则默认为0,表示该参数不用)
返回:无
*/
void myDrawImage(int dstX, int dstY, IMAGE* pSrcImg, int winWidth, int winHeight)
{IMAGE imgTmp;int newImgX = 0;int newImgY = 0;int newImgWidth = pSrcImg->getwidth();int newImgHeight = pSrcImg->getheight();int flag = 0;// 图片完全不在窗口中if (dstX <= -pSrcImg->getwidth()) { return; }if (winWidth > 0 && dstX >= winWidth) { return; }if (dstY <= -pSrcImg->getheight()) { return; }if (winHeight > 0 && dstY >= winHeight) { return; }// 处理X坐标if (dstX < 0)   // 左边界{newImgX = -dstX;newImgWidth = pSrcImg->getwidth() + dstX;dstX = 0;flag = 1;}else if (winWidth > 0 && dstX > winWidth - pSrcImg->getwidth())     // 右边界{newImgWidth = winWidth - dstX;flag = 1;}// 处理Y坐标if (dstY < 0)       // 上边界{newImgY = -dstY;newImgHeight = pSrcImg->getheight() + dstY;dstY = 0;flag = 1;}else if (winHeight > 0 && dstY > winHeight - pSrcImg->getheight())      // 下边界{newImgHeight = winHeight - dstY;flag = 1;}// 处理图片变动if (flag == 1){SetWorkingImage(pSrcImg);   // 设定pSrcImg为当前的绘图设备getimage(&imgTmp, newImgX, newImgY, newImgWidth, newImgHeight);      // 从当前绘图设备获取图像(即截取pSrcImg的要显示部分内容放到imgTmp中)SetWorkingImage();          // 恢复默认绘图设备pSrcImg = &imgTmp;}// 开始输出透明位图myDrawImageAlpha(dstX, dstY, pSrcImg);
}/*
功能:绘制字符串
参数:text - 字符串内容x - X坐标y - Y坐标
返回:无
*/
void myDrawString(int x, int y, const string& text)
{outtextxy(x, y, text.c_str());
}/*
功能:绘制血条
参数:x - 血条X坐标y - 血条Y坐标width - 血条宽度height - 血条高度percent - 血条比例
返回:无
*/
void myDrawBloodBar(int x, int y, int width, int height, float percent);/*
功能:绘制血条
参数:x - 血条X坐标y - 血条Y坐标width - 血条宽度height - 血条高度percent - 血条比例
返回:无
*/
void myDrawBloodBar(int x, int y, int width, int height, float percent)
{// 记录原来的状态,以便后面恢复COLORREF fillColor = getfillcolor();COLORREF getColor = getcolor();if (percent < 0) { percent = 0.0; }setfillcolor(YELLOW);solidrectangle(x, y, x + width, y + height);setfillcolor(RED);solidrectangle(x, y, (int)(x + width * percent), y + height);setcolor(WHITE);rectangle(x, y, x + width, y + height);// 恢复原来的状态setfillcolor(fillColor);setcolor(getColor);
}

方块类 

Block.h

#pragma once#include "lag.h"// 方块类
class Block
{
private:IMAGE imgWall;			// 墙块图片IMAGE imgIron;			// 铁块图片IMAGE imgHome;			// 老巢图片int(*map)[26];			// 当前地图
public:// 构造函数Block();// 绘制方块void draw();};

Block.cpp

#include "Block.h"
#include "GameMap.h"
#include "define.h"// 构造函数
Block::Block()
{// 加载图片loadimage(&imgWall, "./resources/image/wall.gif");loadimage(&imgIron, "./resources/image/iron.gif");loadimage(&imgHome, "./resources/image/home.jpg");// 得到当前地图map = GameMap::getMap();
}// 绘制方块
void Block::draw()
{// 绘制墙块与铁块for (int i = 0; i < 26; i++){for (int j = 0; j < 26; j++){int value = map[i][j];if (value > 0){if (value == WALL)			// 墙块{putimage(j * BLOCK_SIZE, i * BLOCK_SIZE, &imgWall);}else if (value == IRON)	// 铁块{putimage(j * BLOCK_SIZE, i * BLOCK_SIZE, &imgIron);}}}}// 绘制老巢myDrawImage(12 * BLOCK_SIZE, 24 * BLOCK_SIZE, &imgHome);
}

爆炸类

Bomb.h

#pragma once
#include "define.h"
#include "lag.h"// 爆炸类
class Bomb
{
private:static IMAGE* imgBombs[5];				// 爆炸图片public:int x = 0;								// 爆炸中心点X坐标int y = 0;								// 爆炸中心点Y坐标int frameIndex = -1;					// 爆炸图片帧数int maxFrame = 5;						// 最大图片帧数int size = BLOCK_SIZE * 2;				// 爆炸尺寸int status = BOMB_STATUS_FREE;			// 爆炸状态public:// 构造函数Bomb();// 绘制爆炸void draw();
};

Bomb.cpp

#include "Bomb.h"IMAGE* Bomb::imgBombs[5] = {NULL};				// 初始化爆炸图片// 构造函数
Bomb::Bomb()
{// 加载图片if (imgBombs[0] == NULL){IMAGE imgTmp;loadimage(&imgTmp, "./resources/image/bomb.png", size * 5, size);SetWorkingImage(&imgTmp);				// 设定当前绘图目标为imgTmpfor (int i = 0; i < 5; i++){imgBombs[i] = new IMAGE;getimage(imgBombs[i], i * size, 0, size, size);}SetWorkingImage();						// 设置绘图目标为绘图窗口}
}// 绘制爆炸
void Bomb::draw()
{myDrawImage(x - size / 2 + 6, y - size / 2 + 6, imgBombs[frameIndex],GAME_WIDTH,GAME_HEIGHT);	// 得考虑子弹的尺寸
}

子弹类

Bullet.h

#pragma once#include "lag.h"
#include "define.h"// 子弹类
class Bullet
{
private:static IMAGE* imgBullets[4];			// 子弹图片public:int x = 0;								// X坐标int y = 0;								// Y坐标int direction = 0;						// 方向int speed = 6;							// 速度int owner = 0;							// 拥有者int status = BULLET_STATUS_FREE;		// 状态public:// 构造函数Bullet();// 子弹绘制void draw();// 子弹运行void run();};

Bullet.cpp

#include "Bullet.h"IMAGE* Bullet::imgBullets[4] = {NULL};			// 初始化子弹图片// 构造函数
Bullet::Bullet()
{// 加载子弹图片if (imgBullets[0] == NULL){IMAGE imgTmp;loadimage(&imgTmp, "./resources/image/bullet.png", BULLET_SIZE * 4, BULLET_SIZE);SetWorkingImage(&imgTmp);				// 设定当前绘图目标为imgTmpfor (int i = 0; i < 4; i++){imgBullets[i] = new IMAGE;getimage(imgBullets[i], i * BULLET_SIZE, 0, BULLET_SIZE, BULLET_SIZE);}SetWorkingImage();						// 设置绘图目标为绘图窗口}
}// 子弹绘制
void Bullet::draw()
{myDrawImage(this->x, this->y, imgBullets[this->direction], GAME_WIDTH, GAME_HEIGHT);
}// 子弹运行
void Bullet::run()
{switch (this->direction){case TANK_DIRECTION_UP:if (this->y - this->speed >= 0){this->y -= this->speed;}else{this->status = BULLET_STATUS_FREE;}break;case TANK_DIRECTION_DOWN:if (this->y + BULLET_SIZE + this->speed <= GAME_HEIGHT){this->y += this->speed;}else{this->status = BULLET_STATUS_FREE;}break;case TANK_DIRECTION_LEFT:if (this->x >= this->speed){this->x -= this->speed;}else{this->status = BULLET_STATUS_FREE;}break;case TANK_DIRECTION_RIGHT:if (this->x + BLOCK_SIZE + this->speed <= GAME_WIDTH){this->x += this->speed;}else{this->status = BULLET_STATUS_FREE;}break;}
}

宏定义

define.h

#pragma once// 主窗口宏定义
#define TITLE "坦克大战"						// 窗口标题
#define WIN_WIDTH 570						// 窗口宽度
#define WIN_HEIGHT 468						// 窗口高度
#define GAME_WIDTH 468						// 游戏宽度
#define GAME_HEIGHT 468						// 游戏高度
#define INTERVAL_REFRESH 10					// 界面刷新间隔
#define TIMERID_REFRESH 0					// 界面刷新ID
#define GAME_STATUS_RUN	0					// 游戏状态(运行)
#define GAME_STATUS_WIN 1					// 游戏状态(胜利)
#define GAME_STATUS_OVER 2					// 游戏状态(失败)
#define INTERVAL_SCORE 500					// 分数刷新间隔
#define TIMERID_SCORE 8						// 分数刷新ID// 块宏定义
#define BLANK 0								// 空地
#define WALL 1								// 墙块
#define IRON 2								// 铁块
#define HOME 9								// 老巢
#define BLOCK_SIZE 18						// 方块尺寸// 坦克宏定义
#define TANK_OWNER_HERO 8					// 我方坦克
#define TANK_OWNER_ENEMY 7					// 敌方坦克
#define TANK_DIRECTION_UP 0					// 坦克方向(向上)
#define TANK_DIRECTION_LEFT 1				// 坦克方向(向左)
#define TANK_DIRECTION_DOWN 2				// 坦克方向(向下)
#define TANK_DIRECTION_RIGHT 3				// 坦克方向(向右)
#define TANK_STATUS_FREE 0					// 坦克状态(空闲)
#define TANK_STATUS_RUN 1					// 坦克状态(运行)
#define INTERVAL_TANKMOVE 10				// 坦克移动进程刷新间隔
#define TIMERID_TANKMOVE 4					// 坦克移动进程刷新ID
#define INTERVAL_CREATEENEMY 5000			// 敌方坦克投放刷新间隔
#define TIMERID_CREATEENEMY 5				// 敌方坦克投放刷新ID
#define INTERVAL_MOVEENEMY 300				// 敌方坦克移动刷新间隔
#define TIMERID_MOVEENEMY 6					// 敌方坦克移动刷新ID// 子弹宏定义
#define INTERVAL_BULLET_SHOOT 500			// 子弹射击间隔
#define TIMERID_BULLET_SHOOT 1				// 子弹射击ID
#define INTERVAL_BULLET_RUN 10				// 子弹运行间隔
#define TIMERID_BULLET_RUN 2				// 子弹运行ID
#define BULLET_STATUS_FREE 0				// 子弹状态(空闲)
#define BULLET_STATUS_RUN 1					// 子弹状态(运行)
#define BULLET_SIZE 9						// 子弹尺寸// 爆炸宏定义
#define INTERVAL_BOMB 50					// 爆炸刷新间隔
#define TIMERID_BOMB 3						// 爆炸刷新ID
#define BOMB_STATUS_FREE 0					// 爆炸状态(空闲)
#define BOMB_STATUS_RUN 1					// 爆炸状态(运行)// 星星宏定义
#define INTERVAL_STAR 100					// 星星刷新间隔
#define TIMERID_STAR 7						// 星星刷新ID
#define STAR_STATUS_FREE 0					// 星星状态(空闲)
#define STAR_STATUS_RUN 1					// 星星状态(运行)

游戏地图类

GameMap.h

#pragma once#define LEVEL_TOTAL 2						// 总关卡数// 游戏地图
class GameMap
{
private:static int maps[LEVEL_TOTAL][26][26];	// 各关地图static int level;						// 当前关数static int enemyTankNum[LEVEL_TOTAL];	// 当前关敌方坦克数public:// 设置关数static void setLevel(int level);// 得到最大关数static int getMaxLevel();// 得到当前关敌方坦克数static int getEnemyTankNum(int level);// 得到当前关地图static int(*getMap())[26];};

GameMap.cpp

#include "GameMap.h"// 各关地图初始化
int GameMap::maps[LEVEL_TOTAL][26][26] =
{// 第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,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},{0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},{0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},{0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},{0,0,1,1,0,0,1,1,0,0,1,1,2,2,1,1,0,0,1,1,0,0,1,1,0,0},{0,0,1,1,0,0,1,1,0,0,1,1,2,2,1,1,0,0,1,1,0,0,1,1,0,0},{0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},{0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0},{0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0},{0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0},{1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,1,1},{2,2,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2},{0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0},{0,0,1,1,0,0,1,1,0,0,1,1,1,1,1,1,0,0,1,1,0,0,1,1,0,0},{0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},{0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},{0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},{0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0},{0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0},{0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0},{0,0,0,0,0,0,0,0,0,0,0,1,9,9,1,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,1,9,9,1,0,0,0,0,0,0,0,0,0,0,0}},// 第2关地图{{0,0,0,0,0,0,2,2,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,2,2,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0},{0,0,1,1,0,0,2,2,0,0,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0},{0,0,1,1,0,0,2,2,0,0,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0},{0,0,1,1,0,0,0,0,0,0,0,0,1,1,1,1,0,0,1,1,2,2,1,1,0,0},{0,0,1,1,0,0,0,0,0,0,0,0,1,1,1,1,0,0,1,1,2,2,1,1,0,0},{0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0},{0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0},{0,0,0,0,0,0,1,1,0,0,0,0,2,2,0,0,0,0,1,1,0,0,1,1,2,2},{0,0,0,0,0,0,1,1,0,0,0,0,2,2,0,0,0,0,1,1,0,0,1,1,2,2},{0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,2,2,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,2,2,0,0,0,0,0,0,0,0},{0,0,1,1,1,1,1,1,0,0,0,0,0,0,2,2,0,0,0,0,0,0,1,1,0,0},{0,0,1,1,1,1,1,1,0,0,0,0,0,0,2,2,0,0,0,0,0,0,1,1,0,0},{0,0,0,0,0,0,2,2,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},{0,0,0,0,0,0,2,2,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},{2,2,1,1,0,0,2,2,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1,1,0,0},{2,2,1,1,0,0,2,2,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1,1,0,0},{0,0,1,1,0,0,1,1,0,0,1,1,1,1,1,1,0,0,1,1,2,2,1,1,0,0},{0,0,1,1,0,0,1,1,0,0,1,1,1,1,1,1,0,0,1,1,2,2,1,1,0,0},{0,0,1,1,0,0,1,1,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0},{0,0,1,1,0,0,1,1,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0},{0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0},{0,0,1,1,0,0,0,0,0,0,0,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0},{0,0,1,1,0,0,1,1,0,0,0,1,9,9,1,0,0,0,1,1,1,1,1,1,0,0},{0,0,1,1,0,0,1,1,0,0,0,1,9,9,1,0,0,0,1,1,1,1,1,1,0,0}}// 其他关地图...
};// 当前关卡初始化
int GameMap::level = 0;// 当前关敌方坦克数初始化
int GameMap::enemyTankNum[LEVEL_TOTAL] = { 16,20 };// 设置关数
void GameMap::setLevel(int level)
{GameMap::level = level;
}// 得到最大关数
int GameMap::getMaxLevel()
{return LEVEL_TOTAL;
}// 得到当前关敌方坦克数
int GameMap::getEnemyTankNum(int level)
{return GameMap::enemyTankNum[level];
}// 得到当前关地图
int(*GameMap::getMap())[26]
{return GameMap::maps[GameMap::level];
}

星星类

Star.h

#pragma once#include "define.h"
#include "lag.h"// 星星类
class Star
{
private:static IMAGE* imgStars[4];				// 星星图片unsigned long long lastTime;			// 上次时间public:int row = 0;							// 行坐标int column = 0;							// 列坐标int status = STAR_STATUS_FREE;			// 星星状态int frameIndex = -1;					// 星星图片帧数int maxFrame = 4;						// 星星图片帧数int size = BLOCK_SIZE * 2;				// 星星尺寸public:// 构造函数Star();// 绘制星星void draw();};

Star.cpp

#include "Star.h"IMAGE* Star::imgStars[4] = {NULL};// 构造函数
Star::Star()
{// 加载图片if (imgStars[0] == NULL){IMAGE imgTmp;loadimage(&imgTmp, "./resources/image/star.png", size * 4, size);SetWorkingImage(&imgTmp);				// 设定当前绘图目标为imgTmpfor (int i = 0; i < 4; i++){imgStars[i] = new IMAGE;getimage(imgStars[i], i * size, 0, size, size);}SetWorkingImage();						// 设置绘图目标为绘图窗口}// 计时开始this->lastTime = GetTickCount64();
}// 绘制星星
void Star::draw()
{myDrawImage(column * BLOCK_SIZE,row * BLOCK_SIZE, imgStars[frameIndex]);
}

坦克类

Tank.h

#pragma once#include "lag.h"
#include "Bullet.h"// 坦克类
class Tank
{
private:int owner;							// 坦克拥有者int(*map)[26];						// 关卡地图static IMAGE* imgHero[4][4][2];		// 我方坦克图片([装甲][方向][履带])static IMAGE* imgEnemy[4][4][2];	// 敌方坦克图片([装甲][方向][履带])vector bulletPool;			// 子弹池unsigned long long lastTime;		// 上次时间public:int x;								// 坦克左上角X坐标int y;								// 坦克左上角Y坐标int row;							// 坦克所在行int column;							// 坦克所在列int oldRow;							// 坦克上次所在行int oldColumn;						// 坦克上次所在列int direction;						// 坦克方向int speed = 2;						// 坦克速度int originalArmor = 0;				// 原始装甲int armor = 0;						// 当前装甲(共4层)int status = TANK_STATUS_FREE;		// 坦克状态bool track = false;					// 履带切换private:// 坦克是否可以移动bool moveValid(int direction);public:// 有参构造函数Tank(int tankOwner, int tankRow, int tankColumn, int tankDirection);// 坦克绘制void draw();// 坦克移动bool move(int direction);// 坦克射击void shoot();// 坦克坐标映射到地图void toMap();// 坦克重置void reset();// 得到子弹池vector& getBulletPool();};

Tank.cpp

#include "Tank.h"
#include "GameMap.h"
#include "define.h"IMAGE* Tank::imgHero[4][4][2] = {NULL};				// 初始化我方坦克图片
IMAGE* Tank::imgEnemy[4][4][2] = {NULL};			// 初始化敌方坦克图片// 有参构造函数
Tank::Tank(int owner, int row, int column, int direction)
{// 参数初始化this->owner = owner;this->row = row;this->column = column;this->oldRow = this->row;this->oldColumn = this->column;this->x = this->column * BLOCK_SIZE;this->y = this->row * BLOCK_SIZE;this->direction = direction;this->map = GameMap::getMap();// 加载敌我双方坦克图片(图片每一行装甲不同,这里没用装甲)if (imgHero[0][0][0] == NULL){// 加载敌我坦克图片IMAGE imgTmp;loadimage(&imgTmp, "./resources/image/tank.png", BLOCK_SIZE * 16, BLOCK_SIZE * 16);			// 加载时扩大到合适尺寸,剪裁时就正好大小了SetWorkingImage(&imgTmp);			// 切换到绘图工作区for (int ar = 0; ar < 4; ar++){for (int dir = 0; dir < 4; dir++){for (int tr = 0; tr < 2; tr++){// 我方坦克imgHero[ar][dir][tr] = new IMAGE;getimage(imgHero[ar][dir][tr], dir * BLOCK_SIZE * 4 + tr * BLOCK_SIZE * 2 - 1, ar * BLOCK_SIZE * 2, BLOCK_SIZE * 2, BLOCK_SIZE * 2);// 敌方坦克imgEnemy[ar][dir][tr] = new IMAGE;getimage(imgEnemy[ar][dir][tr], dir * BLOCK_SIZE * 4 + tr * BLOCK_SIZE * 2 - 1, BLOCK_SIZE * 8 + ar * BLOCK_SIZE * 2, BLOCK_SIZE * 2, BLOCK_SIZE * 2);}}}SetWorkingImage();					// 切换回窗口工作区}// 计时开始this->lastTime = GetTickCount64();
}// 坦克绘制
void Tank::draw()
{// 绘制我方坦克if (this->owner == TANK_OWNER_HERO && this->status == TANK_STATUS_RUN){myDrawImage(this->x, this->y, imgHero[this->armor][this->direction][this->track]);}// 绘制敌方坦克if (this->owner == TANK_OWNER_ENEMY && this->status == TANK_STATUS_RUN){myDrawImage(this->x, this->y, imgEnemy[this->armor][this->direction][this->track]);}
}// 坦克是否可以移动
bool Tank::moveValid(int direction)
{switch (direction){case TANK_DIRECTION_UP:if (row == 0) { return false; }if (map[row - 1][column] > 0) { return false; }if (map[row - 1][column + 1] > 0) { return false; }break;case TANK_DIRECTION_DOWN:if (row == 24) { return false; }if (map[row + 2][column] > 0) { return false; }if (map[row + 2][column + 1] > 0) { return false; }break;case TANK_DIRECTION_LEFT:if (column == 0) { return false; }if (map[row][column - 1] > 0) { return false; }if (map[row + 1][column - 1] > 0) { return false; }break;case TANK_DIRECTION_RIGHT:if (column == 24) { return false; }if (map[row][column + 2] > 0) { return false; }if (map[row + 1][column + 2] > 0) { return false; }break;}return true;
}// 坦克移动
bool Tank::move(int direction)
{// 如果坦克移动进程未完成则禁止再次移动if (this->oldRow != this->row) { return false; }if (this->oldColumn != this->column) { return false; }if (this->direction == direction)			// 方向相同则前进{//  判断是否可以移动if (!moveValid(direction)) { return false; }// 开始移动switch (direction){case TANK_DIRECTION_UP:map[row + 1][column] = 0;map[row + 1][column + 1] = 0;map[row - 1][column] = owner;map[row - 1][column + 1] = owner;oldRow = row;row--;break;case TANK_DIRECTION_DOWN:map[row][column] = 0;map[row][column + 1] = 0;map[row + 2][column] = owner;map[row + 2][column + 1] = owner;oldRow = row;row++;break;case TANK_DIRECTION_LEFT:map[row][column + 1] = 0;map[row + 1][column + 1] = 0;map[row][column - 1] = owner;map[row + 1][column - 1] = owner;oldColumn = column;column--;break;case TANK_DIRECTION_RIGHT:map[row][column] = 0;map[row + 1][column] = 0;map[row][column + 2] = owner;map[row + 1][column + 2] = owner;oldColumn = column;column++;break;}}else    // 方向不同,仅调整方向不前进{this->direction = direction;}return true;
}// 坦克射击
void Tank::shoot()
{// 射击冷却未到时间禁止发射unsigned long long currentTime = GetTickCount64();int addTime = (int)(currentTime - this->lastTime);if (addTime >= INTERVAL_BULLET_SHOOT){this->lastTime = currentTime;}else{return;}// 在子弹池中寻找空闲的子弹Bullet *bullet = nullptr;int len = this->bulletPool.size();for (int i = 0; i < len; i++){if (this->bulletPool[i].status == BULLET_STATUS_FREE){bullet = &this->bulletPool[i];break;}}if (bullet == nullptr)			// 没有就增加一个{Bullet newBullet;newBullet.owner = this->owner;this->bulletPool.push_back(newBullet);	// 浅拷贝bullet = &bulletPool[len];}// 设置子弹位置bullet->direction = this->direction;switch (this->direction){case TANK_DIRECTION_UP:bullet->x = this->column * BLOCK_SIZE + BLOCK_SIZE - BULLET_SIZE / 2;bullet->y = this->row * BLOCK_SIZE - BULLET_SIZE - 4;break;case TANK_DIRECTION_DOWN:bullet->x = this->column * BLOCK_SIZE + BLOCK_SIZE - BULLET_SIZE / 2;bullet->y = this->row * BLOCK_SIZE + 2 * BLOCK_SIZE + 4;break;case TANK_DIRECTION_LEFT:bullet->x = this->column * BLOCK_SIZE - BULLET_SIZE - 4;bullet->y = this->row * BLOCK_SIZE + BLOCK_SIZE - BULLET_SIZE / 2;break;case TANK_DIRECTION_RIGHT:bullet->x = this->column * BLOCK_SIZE + 2 * BLOCK_SIZE + 4;bullet->y = this->row * BLOCK_SIZE + BLOCK_SIZE - BULLET_SIZE / 2;break;}// 设置子弹状态bullet->status = BULLET_STATUS_RUN;// 我方坦克发出射击声音if (owner == TANK_OWNER_HERO){myPlayAudio("./resources/audio/shoot.mp3");}}// 坦克坐标映射到地图
void Tank::toMap()
{map[row][column] = owner;map[row][column + 1] = owner;map[row + 1][column] = owner;map[row + 1][column + 1] = owner;
}// 坦克重置
void Tank::reset()
{map[row][column] = 0;map[row][column + 1] = 0;map[row + 1][column] = 0;map[row + 1][column + 1] = 0;if (owner == TANK_OWNER_HERO){this->row = 24;this->column = 8;this->oldRow = this->row;this->oldColumn = this->column;this->x = this->column * BLOCK_SIZE;this->y = this->row * BLOCK_SIZE;this->direction = TANK_DIRECTION_UP;this->armor = 3;}else{this->row = 0;this->oldRow = 0;this->column = 0;this->oldColumn = 0;this->x = 0;this->y = 0;}this->status = TANK_STATUS_FREE;
}// 得到子弹池
vector& Tank::getBulletPool()
{return this->bulletPool;
}

我方坦克类

Hero.h

#pragma once#include "Tank.h"// 我方坦克类
class Hero : public Tank
{public:// 构造函数Hero(int tankRow, int tankColumn, int tankDirection);};

Hero.cpp

#include "Hero.h"
#include "define.h"// 构造函数
Hero::Hero(int tankRow, int tankColumn, int tankDirection) : Tank(TANK_OWNER_HERO, tankRow, tankColumn, tankDirection) {}

敌方坦克类

Enemy.h

#pragma once
#include "Tank.h"// 敌方坦克类
class Enemy : public Tank
{
public:int target = HOME;		// 坦克射击的目标(我方坦克,老巢)public:// 构造函数Enemy(int tankRow, int tankColumn, int tankDirection);};

Enemy.cpp

#include "Enemy.h"// 构造函数
Enemy::Enemy(int tankRow, int tankColumn, int tankDirection) : Tank::Tank(TANK_OWNER_ENEMY, tankRow, tankColumn, tankDirection){}

main.cpp

#include "lag.h"
#include "define.h"
#include "GameMap.h"
#include "Block.h"
#include "Hero.h"
#include "Enemy.h"
#include "Bullet.h"
#include "Bomb.h"
#include "Star.h"// 项目结构
struct _stu_project
{int gameStatus = GAME_STATUS_RUN;					// 游戏状态bool refreshFlag = false;							// 刷新标识int level = 0;										// 游戏关卡int enemyMaxNum = 0;								// 敌方坦克最大数量int enemyCreateNum = 0;								// 敌方坦克投放数量int enemyDeadNum = 0;								// 敌方坦克死亡数量int enemyArmorDeadNum[4] = {0,0,0,0};				// 敌方不同装甲坦克死亡数量IMAGE imgLogo;										// Logo图片IMAGE imgLevel;										// 关卡图片IMAGE imgScore;										// 分数图片IMAGE imgSuccess;									// 游戏成功图片IMAGE imgFailure;									// 游戏失败图片int(*map)[26] = nullptr;							// 关卡地图} stu_project;// 子弹所在行列结构
struct _stu_bullet_pos
{int row1;int col1;int row2;int col2;
};// 方块类
Block block;// 我方坦克类
Hero hero(24, 8, TANK_DIRECTION_UP);// 我方子弹池
vector& heroBulletPool = hero.getBulletPool();// 敌方坦克类
vector enemyPool;// 爆炸池
vector bombPool;// 星星池
vector starPool;// 函数声明
void createWindow();
void welcome();
void loadResources();
void initGame();
void paint();
void run();
void keyEvent();
void mouseEvent();
void starCreate();
void starAdd(int row, int column);
void enemyTankCreate(int row, int column);
void enemyTankMove();
void enemyTankShoot();
int enemyTankAI(Enemy& enemy);
void bombAdd(int x, int y);
void collide();
void doGame();
void newGame();
void showScore();
void getBulletPos(struct _stu_bullet_pos* pos, int x, int y, int direction);// 坦克大战
int main()
{// 创建窗口createWindow();// 显示封面welcome();// 开始游戏newGame();// 暂停一下system("pause");// 关闭窗口closegraph();return 0;
}// 创建窗口
void createWindow()
{// 创建并得到窗口句柄HWND hWnd = initgraph(WIN_WIDTH, WIN_HEIGHT);// 修改标题SetWindowText(hWnd, TITLE);// 居中显示myCenterWindow(hWnd);
}// 加载资源
void loadResources()
{// 加载游戏成功图片loadimage(&stu_project.imgSuccess, "./resources/image/success.png");// 加载游戏失败图片loadimage(&stu_project.imgFailure, "./resources/image/failure.png");// 加载关卡图片loadimage(&stu_project.imgLevel, "./resources/image/level.png");// 加载Logo图片loadimage(&stu_project.imgLogo, "./resources/image/logo.png");// 加载分数图片loadimage(&stu_project.imgScore, "./resources/image/score.jpg");
}// 显示封面
void welcome()
{// 显示封面IMAGE imgWelcome;loadimage(&imgWelcome, "./resources/image/welcome.gif");putimage(0, 0, &imgWelcome);// 加载资源loadResources();// 暂停一下system("pause");
}// 初始化游戏
void initGame()
{// 项目参数初始化stu_project.gameStatus = GAME_STATUS_RUN;stu_project.refreshFlag = false;stu_project.enemyMaxNum = GameMap::getEnemyTankNum(stu_project.level);stu_project.enemyCreateNum = 0;stu_project.enemyDeadNum = 0;stu_project.map = GameMap::getMap();for (int i = 0; i < 4; i++){stu_project.enemyArmorDeadNum[i] = 0;}// 设置游戏关卡GameMap::setLevel(stu_project.level);// 我方坦克初始化hero.reset();hero.status = TANK_STATUS_RUN;hero.toMap();// 敌方坦克池初始化10辆坦克int lenEnemyPool = enemyPool.size();if (lenEnemyPool == 0){for (int i = 0; i < 10; i++){Enemy enemy(0,0,TANK_DIRECTION_DOWN);enemyPool.push_back(enemy);}}else{for (int i = 0; i < lenEnemyPool; i++){enemyPool[i].reset();vector& enemyBulletPool = enemyPool[i].getBulletPool();int lenEnemyBulletPool = enemyBulletPool.size();for (int j = 0; j < lenEnemyBulletPool; j++) { enemyBulletPool[j].status = BULLET_STATUS_FREE; }}}// 星星初始化int lenStarPool = starPool.size();if (lenStarPool == 0){for (int i = 0; i < 3; i++){Star star;starPool.push_back(star);}}else{for (int i = 0; i < lenStarPool; i++) { starPool[i].status = STAR_STATUS_FREE; }}// 爆炸初始化int lenBombPool = bombPool.size();for (int i = 0; i < lenBombPool; i++) {	bombPool[i].status = BOMB_STATUS_FREE;}}// 绘制窗口
void paint()
{// 开启双缓冲机制,防止闪烁BeginBatchDraw();// 绘制游戏区域setcolor(BLACK);cleardevice();setfillcolor(LIGHTGRAY);fillrectangle(GAME_WIDTH, -2, WIN_WIDTH, WIN_HEIGHT + 2);// 绘制分数settextstyle(20, 0, "微软雅黑");settextcolor(YELLOW);setbkmode(TRANSPARENT);myDrawString(GAME_WIDTH + 20, 6, "总数:" + to_string(stu_project.enemyMaxNum));myDrawString(GAME_WIDTH + 20, 26, "死亡:" + to_string(stu_project.enemyDeadNum));myDrawString(GAME_WIDTH + 20, 46, "剩余:" + to_string(stu_project.enemyMaxNum - stu_project.enemyDeadNum));int notCreateNum = stu_project.enemyMaxNum - stu_project.enemyCreateNum;if (notCreateNum > 0){int logoRow = notCreateNum / 2;if (notCreateNum % 2 != 0) { logoRow++; }int logoCount = 0;for (int i = 0; i < logoRow; i++){for (int j = 0; j < 2; j++){if (logoCount >= notCreateNum) { break; }myDrawImage(GAME_WIDTH + 22 + j * (20 + 14), 80 + i * (16 + 4), &stu_project.imgLogo,WIN_WIDTH,WIN_HEIGHT);logoCount++;}}}myDrawImage(GAME_WIDTH + 20, 330, &stu_project.imgLevel);settextcolor(RED);myDrawString(GAME_WIDTH + 20, 380, "关数:" + to_string(stu_project.level + 1));settextcolor(WHITE);myDrawString(GAME_WIDTH + 20, 420, "作者:Lag");// 绘制地图block.draw();// 绘制我方坦克hero.draw();// 绘制我方子弹int lenHeroBulletPool = heroBulletPool.size();for (int i = 0; i < lenHeroBulletPool; i++){if (heroBulletPool[i].status == BULLET_STATUS_RUN){heroBulletPool[i].draw();}}// 绘制敌方坦克、子弹int lenEnemyPool = enemyPool.size();for (int i = 0; i < lenEnemyPool; i++){if (enemyPool[i].status == TANK_STATUS_RUN) { enemyPool[i].draw(); }vector& enemyBulletPool = enemyPool[i].getBulletPool();int lenEnemyBulletPool = enemyBulletPool.size();for (int j = 0; j < lenEnemyBulletPool; j++){if (enemyBulletPool[j].status == BULLET_STATUS_RUN){enemyBulletPool[j].draw();}}}// 绘制爆炸int lenBombPool = bombPool.size();for (int i = 0; i < lenBombPool; i++){if (bombPool[i].status == BOMB_STATUS_RUN){bombPool[i].draw();}}// 绘制星星int lenStarPool = starPool.size();for (int i = 0; i < lenStarPool; i++){if (starPool[i].status == STAR_STATUS_RUN){starPool[i].draw();}}// 结束双缓冲机制EndBatchDraw();
}// 向星星池增加
void starAdd(int row, int column)
{// 流星雨myPlayAudio("./resources/audio/star.mp3");// 查找空闲的星星int len = starPool.size();for (int i = 0; i < len; i++){if (starPool[i].status == STAR_STATUS_FREE){starPool[i].row = row;starPool[i].column = column;starPool[i].frameIndex = 0;starPool[i].status = STAR_STATUS_RUN;break;}}
}// 星星创建(在坦克创建前先创建星星)
void starCreate()
{// 创建坦克标识bool createFlag = false;if (stu_project.enemyCreateNum == 0 || myTimer(INTERVAL_CREATEENEMY, TIMERID_CREATEENEMY)) { createFlag = true; }if (createFlag){// 判断投放地点是否为空int createPos[3] = { -1,-1,-1 };int createPosNum = 0;if (stu_project.map[0][0] == BLANK && stu_project.map[0][1] == BLANK && stu_project.map[1][0] == BLANK && stu_project.map[1][1] == BLANK)			// 判断左上角位置是否可以投放{createPos[createPosNum] = 0;createPosNum++;}if (stu_project.map[0][12] == BLANK && stu_project.map[0][13] == BLANK && stu_project.map[1][12] == BLANK && stu_project.map[1][13] == BLANK)		// 判断中间位置是否可以投放{createPos[createPosNum] = 12;createPosNum++;}if (stu_project.map[0][24] == BLANK && stu_project.map[0][25] == BLANK && stu_project.map[1][24] == BLANK && stu_project.map[1][25] == BLANK)		// 判断右上角位置是否可以投放{createPos[createPosNum] = 24;createPosNum++;}if (createPosNum < 1) { return; }		// 无投放位置,退出// 首次一次性投放3辆坦克if (stu_project.enemyCreateNum == 0){for (int i = 0; i < createPosNum; i++){// 先把茅坑占上stu_project.map[0][createPos[i]] = 999;stu_project.map[0][createPos[i] + 1] = 999;stu_project.map[1][createPos[i]] = 999;stu_project.map[1][createPos[i] + 1] = 999;// 增加星星starAdd(0, createPos[i]);}}else     // 每隔一段时间投放一辆坦克{// 判断是否达到最大投放数量if (stu_project.enemyCreateNum >= stu_project.enemyMaxNum) { return; }// 得到随机投放位置int pos = createPos[myRandom(0, createPosNum - 1)];// 先把茅坑占上stu_project.map[0][pos] = 999;stu_project.map[0][pos + 1] = 999;stu_project.map[1][pos] = 999;stu_project.map[1][pos + 1] = 999;// 增加星星starAdd(0, pos);}}
}// 敌方坦克创建(在星星创建的位置)
void enemyTankCreate(int row,int column)
{// 在坦克池中寻找空闲的坦克Enemy* enemy = nullptr;int lenEnemyPool = enemyPool.size();for (int i = 0; i < lenEnemyPool; i++){if (enemyPool[i].status == TANK_STATUS_FREE){enemy = &enemyPool[i];break;}}if (enemy == nullptr)			// 没有就增加一个{Enemy newEnemy(0, 0, TANK_DIRECTION_DOWN);enemyPool.push_back(newEnemy);enemy = &enemyPool[lenEnemyPool];}// 开始投放enemy->row = row;enemy->oldRow = enemy->row;enemy->column = column;enemy->oldColumn = enemy->column;enemy->x = enemy->column * BLOCK_SIZE;enemy->y = 0;enemy->direction = TANK_DIRECTION_DOWN;if (stu_project.enemyCreateNum >= 3){enemy->armor = myRandom(0, 3);}else{enemy->armor = 0;}enemy->originalArmor = enemy->armor;enemy->target = myRandom(TANK_OWNER_HERO, HOME);enemy->status = TANK_STATUS_RUN;enemy->toMap();stu_project.enemyCreateNum++;}// 敌方坦克AI(返回方向)
// 原理:判断目标与敌方坦克的相应位置,找出哪个方向最可能靠近目标。
int enemyTankAI(Enemy &enemy)
{// 先剔除墙与铁块那个方向(铁块暂时打不动)int validDirectory[4][2] = { -1,-1,-1,-1,-1,-1,-1,-1 };		// 可用方向数组[方向][空地/墙]int validDirectoryNum = 0;for (int i = 0; i < 4; i++){bool blankFlag = false;		// 空地标识(true-是空地,false-非空地)if (i == TANK_DIRECTION_UP){if (enemy.row == 0){continue;}if (stu_project.map[enemy.row - 1][enemy.column] == IRON || stu_project.map[enemy.row - 1][enemy.column + 1] == IRON) { continue; }if (stu_project.map[enemy.row - 1][enemy.column] == BLANK && stu_project.map[enemy.row - 1][enemy.column + 1] == BLANK) { blankFlag = true; }}else if (i == TANK_DIRECTION_DOWN){if (enemy.row == 24) { continue; }if (stu_project.map[enemy.row + 2][enemy.column] == IRON || stu_project.map[enemy.row + 2][enemy.column + 1] == IRON) { continue; }if (stu_project.map[enemy.row + 2][enemy.column] == BLANK && stu_project.map[enemy.row + 2][enemy.column + 1] == BLANK) { blankFlag = true; }}else if (i == TANK_DIRECTION_LEFT){if (enemy.column == 0) { continue; }if (stu_project.map[enemy.row][enemy.column - 1] == IRON || stu_project.map[enemy.row + 1][enemy.column - 1] == IRON) { continue; }if (stu_project.map[enemy.row][enemy.column - 1] == BLANK && stu_project.map[enemy.row + 1][enemy.column - 1] == BLANK) { blankFlag = true; }}else if (i == TANK_DIRECTION_RIGHT){if (enemy.column == 24) { continue; }if (stu_project.map[enemy.row][enemy.column + 2] == IRON || stu_project.map[enemy.row + 1][enemy.column + 2] == IRON) { continue; }if (stu_project.map[enemy.row][enemy.column + 2] == BLANK && stu_project.map[enemy.row + 1][enemy.column + 2] == BLANK) { blankFlag = true; }}validDirectory[validDirectoryNum][0] = i;if (blankFlag){validDirectory[validDirectoryNum][1] = BLANK;}else{validDirectory[validDirectoryNum][1] = WALL;}validDirectoryNum++;}// 判断目标与敌方坦克位置关系int directory[4] = { -1,-1,-1,-1 };int targetRow;int targetColumn;if (enemy.target == TANK_OWNER_HERO)	// 目标是我方坦克{targetRow = hero.row;targetColumn = hero.column;}else   // 目标是老巢{targetRow = 24;targetColumn = 12;}if (targetColumn > enemy.column)		// 目标在敌方坦克右边{if (targetRow < enemy.row)			// 右上角(上或右){for (int i = 0; i < validDirectoryNum; i++){if (validDirectory[i][0] == TANK_DIRECTION_UP || validDirectory[i][0] == TANK_DIRECTION_RIGHT){// 空地优先级别高if (validDirectory[i][1] == BLANK){if (directory[0] != -1){directory[1] = directory[0];}directory[0] = validDirectory[i][0];continue;}// 从左向右填充for (int j = 0; j < 4; j++){if (directory[j] == -1){directory[j] = validDirectory[i][0];break;}}}else{// 从右向左填充for (int j = 3; j >= 0; j--){if (directory[j] == -1){directory[j] = validDirectory[i][0];break;}}}}}else     // 右下角(下或右){for (int i = 0; i < validDirectoryNum; i++){if (validDirectory[i][0] == TANK_DIRECTION_DOWN || validDirectory[i][0] == TANK_DIRECTION_RIGHT){// 空地优先级别高if (validDirectory[i][1] == BLANK){if (directory[0] != -1) { directory[1] = directory[0]; }directory[0] = validDirectory[i][0];continue;}// 从左向右填充for (int j = 0; j < 4; j++){if (directory[j] == -1){directory[j] = validDirectory[i][0];break;}}}else{// 从右向左填充for (int j = 3; j >= 0; j--){if (directory[j] == -1){directory[j] = validDirectory[i][0];break;}}}}}// 特殊情况(同一条线上)if (targetRow == enemy.row){for (int i = 0; i < validDirectoryNum; i++){if (validDirectory[i][0] == TANK_DIRECTION_RIGHT){if (validDirectory[i][1] == BLANK){return validDirectory[i][0];}		// 是空地就必须勇敢的冲过去}}}}else									// 目标在敌方坦克左边{if (targetRow < enemy.row)			// 左上角(上或左){for (int i = 0; i < validDirectoryNum; i++){if (validDirectory[i][0] == TANK_DIRECTION_UP || validDirectory[i][0] == TANK_DIRECTION_LEFT){// 空地优先级别高if (validDirectory[i][1] == BLANK){if (directory[0] != -1) { directory[1] = directory[0]; }directory[0] = validDirectory[i][0];continue;}// 从左向右填充for (int j = 0; j < 4; j++){if (directory[j] == -1){directory[j] = validDirectory[i][0];break;}}}else{// 从右向左填充for (int j = 3; j >= 0; j--){if (directory[j] == -1){directory[j] = validDirectory[i][0];break;}}}}}else     // 左下角(下或左){for (int i = 0; i < validDirectoryNum; i++){if (validDirectory[i][0] == TANK_DIRECTION_DOWN || validDirectory[i][0] == TANK_DIRECTION_LEFT){// 空地优先级别高if (validDirectory[i][1] == BLANK){if (directory[0] != -1) { directory[1] = directory[0]; }directory[0] = validDirectory[i][0];continue;}// 从左向右填充for (int j = 0; j < 4; j++){if (directory[j] == -1){directory[j] = validDirectory[i][0];break;}}}else{// 从右向左填充for (int j = 3; j >= 0; j--){if (directory[j] == -1){directory[j] = validDirectory[i][0];break;}}}}}// 特殊情况(同一条线上)if (targetRow == enemy.row){for (int i = 0; i < validDirectoryNum; i++){if (validDirectory[i][0] == TANK_DIRECTION_LEFT){if (validDirectory[i][1] == BLANK) { return validDirectory[i][0]; }		// 是空地就必须勇敢的冲过去}}}}if (targetColumn == enemy.column)	// 目标在敌方坦克正上或下方{// 特殊情况(同一条线上)if (targetRow < enemy.row)		// 正上方{for (int i = 0; i < validDirectoryNum; i++){if (validDirectory[i][0] == TANK_DIRECTION_UP){if (validDirectory[i][1] == BLANK) { return validDirectory[i][0]; }		// 是空地就必须勇敢的冲过去}}}else{for (int i = 0; i < validDirectoryNum; i++){if (validDirectory[i][0] == TANK_DIRECTION_DOWN){if (validDirectory[i][1] == BLANK) {return validDirectory[i][0]; }		// 是空地就必须勇敢的冲过去}}}}// 方向补全for (int i = 0; i < 4; i++){if (directory[i] == -1) { directory[i] = enemy.direction; }}// 随机抽取4:3:2:1int random = myRandom(1,10);if (random <= 4){return directory[0];}else if (random <= 7){return directory[1];}else if (random <= 9){return directory[2];}else{return directory[3];}}// 敌方坦克移动
void enemyTankMove()
{if (myTimer(INTERVAL_MOVEENEMY, TIMERID_MOVEENEMY)){int lenEnemyPool = enemyPool.size();for (int i = 0; i < lenEnemyPool; i++){if (enemyPool[i].status == TANK_STATUS_RUN){// 得到移动方向enemyPool[i].direction = enemyTankAI(enemyPool[i]);enemyPool[i].move(enemyPool[i].direction);stu_project.refreshFlag = true;}}}
}// 敌方坦克射击
void enemyTankShoot()
{if (myTimer(INTERVAL_BULLET_SHOOT, TIMERID_BULLET_SHOOT)){int lenEnemyPool = enemyPool.size();for (int i = 0; i < lenEnemyPool; i++){if (enemyPool[i].status == TANK_STATUS_RUN){// 敌方坦克比较傻,不爱射击if (myRandom(1, 10) < 4){enemyPool[i].shoot();}			// 30%几率开枪stu_project.refreshFlag = true;}}}
}// 向爆炸池增加
void bombAdd(int x, int y)
{// 查找空闲的爆炸Bomb* bomb = nullptr;int len = bombPool.size();for (int i = 0; i < len; i++){if (bombPool[i].status == BOMB_STATUS_FREE){bomb = &bombPool[i];break;}}if (bomb == nullptr)				// 没有就增加一个{Bomb newBomb;bombPool.push_back(newBomb);bomb = &bombPool[len];}bomb->x = x;bomb->y = y;bomb->frameIndex = 0;bomb->status = BOMB_STATUS_RUN;
}// 得到子弹所在行列位置(子弹位于坦克中间位置,所以正对着2个格子的中间位置,所以计算时2个格子都在其射击范围内)
void getBulletPos(struct _stu_bullet_pos* pBulletPos, int x, int y, int direction)
{int x1, y1, x2, y2;// 重算子弹图片中心点坐标(因为此时的x与y是子弹图片左上角的坐标)x = x + BULLET_SIZE / 2;y = y + BULLET_SIZE / 2;// 根据方向判断2个格子位置if (direction == TANK_DIRECTION_UP || direction == TANK_DIRECTION_DOWN){x1 = x - BLOCK_SIZE / 2;y1 = y;x2 = x + BLOCK_SIZE / 2;y2 = y;}else{x1 = x;y1 = y - BLOCK_SIZE / 2;x2 = x;y2 = y + BLOCK_SIZE / 2;}pBulletPos->row1 = y1 / BLOCK_SIZE;pBulletPos->col1 = x1 / BLOCK_SIZE;pBulletPos->row2 = y2 / BLOCK_SIZE;pBulletPos->col2 = x2 / BLOCK_SIZE;
}// 碰撞处理
void collide()
{// 子弹所在位置结构体struct _stu_bullet_pos stu_bullet_pos;// 判断我方子弹是否与敌方坦克或块或老巢碰撞int lenHeroBulletPool = heroBulletPool.size();for (int i = 0; i < lenHeroBulletPool; i++){if (heroBulletPool[i].status == BULLET_STATUS_RUN){// 得到子弹所在行与列getBulletPos(&stu_bullet_pos, heroBulletPool[i].x, heroBulletPool[i].y, heroBulletPool[i].direction);// 得到子弹2个点的值int v1 = stu_project.map[stu_bullet_pos.row1][stu_bullet_pos.col1];int v2 = stu_project.map[stu_bullet_pos.row2][stu_bullet_pos.col2];if (v1 == 0 && v2 == 0) { continue; }if (v1 == TANK_OWNER_HERO && v2 == TANK_OWNER_HERO) { continue; }// 判断是否与墙块碰撞if (v1 == WALL) { stu_project.map[stu_bullet_pos.row1][stu_bullet_pos.col1] = 0; }if (v2 == WALL) { stu_project.map[stu_bullet_pos.row2][stu_bullet_pos.col2] = 0; }// 判断是否与老巢碰撞if (v1 == HOME || v2 == HOME) { stu_project.gameStatus = GAME_STATUS_OVER; }// 判断是否与敌方坦克碰撞int lenEnemyPool = enemyPool.size();for (int i = 0; i < lenEnemyPool; i++){if (enemyPool[i].status == TANK_STATUS_RUN) {if ((enemyPool[i].row == stu_bullet_pos.row1 && enemyPool[i].column == stu_bullet_pos.col1) || (enemyPool[i].row == stu_bullet_pos.row1 && enemyPool[i].column + 1 == stu_bullet_pos.col1) || (enemyPool[i].row + 1 == stu_bullet_pos.row1 && enemyPool[i].column == stu_bullet_pos.col1) ||(enemyPool[i].row + 1 == stu_bullet_pos.row1 && enemyPool[i].column + 1 == stu_bullet_pos.col1) ||(enemyPool[i].row == stu_bullet_pos.row2 && enemyPool[i].column == stu_bullet_pos.col2) ||(enemyPool[i].row == stu_bullet_pos.row2 && enemyPool[i].column + 1 == stu_bullet_pos.col2) ||(enemyPool[i].row + 1 == stu_bullet_pos.row2 && enemyPool[i].column == stu_bullet_pos.col2) ||(enemyPool[i].row + 1 == stu_bullet_pos.row2 && enemyPool[i].column + 1 == stu_bullet_pos.col2)){myPlayAudio("./resources/audio/boom.wav");if (enemyPool[i].armor == 0){enemyPool[i].reset();stu_project.enemyDeadNum++;stu_project.enemyArmorDeadNum[enemyPool[i].originalArmor]++;if (stu_project.enemyDeadNum >= stu_project.enemyMaxNum){stu_project.gameStatus = GAME_STATUS_WIN;}break;}else{enemyPool[i].armor--;}}}}// 子弹销毁heroBulletPool[i].status = BULLET_STATUS_FREE;// 子弹爆炸bombAdd(heroBulletPool[i].x, heroBulletPool[i].y);}}// 判断敌方子弹是否与我方坦克或块或老巢碰撞(不需要判断敌方坦克状态,因为即使坦克销毁子弹可能仍然在飞)int lenEnemyPool = enemyPool.size();for (int m = 0; m < lenEnemyPool; m++){vector& enemyBulletPool = enemyPool[m].getBulletPool();int lenEnemyBulletPool = enemyBulletPool.size();for (int i = 0; i < lenEnemyBulletPool; i++){if (enemyBulletPool[i].status == BULLET_STATUS_RUN){// 得到子弹所在行与列getBulletPos(&stu_bullet_pos, enemyBulletPool[i].x, enemyBulletPool[i].y, enemyBulletPool[i].direction);// 得到子弹2个点的值int v1 = stu_project.map[stu_bullet_pos.row1][stu_bullet_pos.col1];int v2 = stu_project.map[stu_bullet_pos.row2][stu_bullet_pos.col2];if (v1 == 0 && v2 == 0) { continue; }if (v1 == TANK_OWNER_ENEMY && v2 == TANK_OWNER_ENEMY) { continue; }// 判断是否与墙块碰撞if (v1 == WALL) { stu_project.map[stu_bullet_pos.row1][stu_bullet_pos.col1] = 0; }if (v2 == WALL) { stu_project.map[stu_bullet_pos.row2][stu_bullet_pos.col2] = 0; }// 判断是否与老巢碰撞if (v1 == HOME || v2 == HOME) { stu_project.gameStatus = GAME_STATUS_OVER; }// 判断是否与我方坦克碰撞if (hero.status == TANK_STATUS_RUN){if ((hero.row == stu_bullet_pos.row1 && hero.column == stu_bullet_pos.col1) ||(hero.row == stu_bullet_pos.row1 && hero.column + 1 == stu_bullet_pos.col1) ||(hero.row + 1 == stu_bullet_pos.row1 && hero.column == stu_bullet_pos.col1) ||(hero.row + 1 == stu_bullet_pos.row1 && hero.column + 1 == stu_bullet_pos.col1) ||(hero.row == stu_bullet_pos.row2 && hero.column == stu_bullet_pos.col2) ||(hero.row == stu_bullet_pos.row2 && hero.column + 1 == stu_bullet_pos.col2) ||(hero.row + 1 == stu_bullet_pos.row2 && hero.column == stu_bullet_pos.col2) ||(hero.row + 1 == stu_bullet_pos.row2 && hero.column + 1 == stu_bullet_pos.col2)){if (hero.armor == 0){hero.reset();stu_project.gameStatus = GAME_STATUS_OVER;}else{hero.armor--;}}}// 子弹销毁enemyBulletPool[i].status = BULLET_STATUS_FREE;// 子弹爆炸bombAdd(enemyBulletPool[i].x, enemyBulletPool[i].y);}}}}// 游戏进程
void run()
{// 重置刷新标识stu_project.refreshFlag = false;// 星星创建starCreate();// 星星进程if (myTimer(INTERVAL_STAR, TIMERID_STAR)){int len = starPool.size();for (int i = 0; i < len; i++){if (starPool[i].status == STAR_STATUS_RUN){int index = starPool[i].frameIndex;if (index >= starPool[i].maxFrame - 1){starPool[i].status = STAR_STATUS_FREE;enemyTankCreate(starPool[i].row, starPool[i].column);		// 创建敌方坦克continue;}starPool[i].frameIndex = ++index;}}}// 敌方坦克移动enemyTankMove();// 敌方坦克射击enemyTankShoot();// 坦克移动进程if (myTimer(INTERVAL_TANKMOVE, TIMERID_TANKMOVE)){// 我方坦克移动if (hero.oldRow != hero.row){int destY = hero.row * BLOCK_SIZE;if (hero.direction == TANK_DIRECTION_UP){if (hero.y > destY){hero.y = hero.y - hero.speed;hero.track = !hero.track;		// 切换履带}else{hero.oldRow = hero.row;hero.y = destY;					// 坐标微调}}else if (hero.direction == TANK_DIRECTION_DOWN){if (hero.y < destY){hero.y = hero.y + hero.speed;hero.track = !hero.track;}else{hero.oldRow = hero.row;hero.y = destY;}}stu_project.refreshFlag = true;}else if (hero.column != hero.oldColumn){int destX = hero.column * BLOCK_SIZE;if (hero.direction == TANK_DIRECTION_LEFT){if (hero.x > destX){hero.x = hero.x - hero.speed;hero.track = !hero.track;}else{hero.oldColumn = hero.column;hero.x = destX;}}else if (hero.direction == TANK_DIRECTION_RIGHT){if (hero.x < destX){hero.x = hero.x + hero.speed;hero.track = !hero.track;}else{hero.oldColumn = hero.column;hero.x = destX;}}stu_project.refreshFlag = true;}// 敌方坦克移动int lenEnemyPool = enemyPool.size();for (int i = 0; i < lenEnemyPool; i++){if (enemyPool[i].status == TANK_STATUS_RUN){if (enemyPool[i].oldRow != enemyPool[i].row){int destY = enemyPool[i].row * BLOCK_SIZE;if (enemyPool[i].direction == TANK_DIRECTION_UP){if (enemyPool[i].y > destY){enemyPool[i].y = enemyPool[i].y - enemyPool[i].speed;enemyPool[i].track = !enemyPool[i].track;		// 切换履带}else{enemyPool[i].oldRow = enemyPool[i].row;enemyPool[i].y = destY;					// 坐标微调}}else if (enemyPool[i].direction == TANK_DIRECTION_DOWN){if (enemyPool[i].y < destY){enemyPool[i].y = enemyPool[i].y + enemyPool[i].speed;enemyPool[i].track = !enemyPool[i].track;}else{enemyPool[i].oldRow = enemyPool[i].row;enemyPool[i].y = destY;}}stu_project.refreshFlag = true;}else if (enemyPool[i].column != enemyPool[i].oldColumn){int destX = enemyPool[i].column * BLOCK_SIZE;if (enemyPool[i].direction == TANK_DIRECTION_LEFT){if (enemyPool[i].x > destX){enemyPool[i].x = enemyPool[i].x - enemyPool[i].speed;enemyPool[i].track = !enemyPool[i].track;}else{enemyPool[i].oldColumn = enemyPool[i].column;enemyPool[i].x = destX;}}else if (enemyPool[i].direction == TANK_DIRECTION_RIGHT){if (enemyPool[i].x < destX){enemyPool[i].x = enemyPool[i].x + enemyPool[i].speed;enemyPool[i].track = !enemyPool[i].track;}else{enemyPool[i].oldColumn = enemyPool[i].column;enemyPool[i].x = destX;}}stu_project.refreshFlag = true;}}}}// 子弹进程if (myTimer(INTERVAL_BULLET_RUN, TIMERID_BULLET_RUN)){// 我方子弹运行int lenHeroBulletPool = heroBulletPool.size();for (int i = 0; i < lenHeroBulletPool; i++){if (heroBulletPool[i].status == BULLET_STATUS_RUN){heroBulletPool[i].run();}}// 敌方子弹运行int lenEnemyPool = enemyPool.size();for (int i = 0; i < lenEnemyPool; i++){vector& enemyBulletPool = enemyPool[i].getBulletPool();int lenEnemyBulletPool = enemyBulletPool.size();for (int j = 0; j < lenEnemyBulletPool; j++){if (enemyBulletPool[j].status == BULLET_STATUS_RUN){enemyBulletPool[j].run();}}}}// 碰撞处理collide();// 爆炸进程if (myTimer(INTERVAL_BOMB, TIMERID_BOMB)){int len = bombPool.size();for (int i = 0; i < len; i++){if (bombPool[i].status == BOMB_STATUS_RUN){int index = bombPool[i].frameIndex;if (index >= bombPool[i].maxFrame - 1){bombPool[i].status = BOMB_STATUS_FREE;break;}bombPool[i].frameIndex = ++index;}}}// 刷新界面if (stu_project.refreshFlag) { paint(); }}// 键盘监控
void keyEvent()
{// 重置刷新标识stu_project.refreshFlag = false;// 方向键只运行同时按一个,用else if判断if (myKeyDown(VK_UP))				// 上键{if (hero.move(TANK_DIRECTION_UP)) { stu_project.refreshFlag = true; }}else if (myKeyDown(VK_DOWN))		// 下键{if (hero.move(TANK_DIRECTION_DOWN)) { stu_project.refreshFlag = true; }}else if (myKeyDown(VK_LEFT))		// 左键{if (hero.move(TANK_DIRECTION_LEFT)) { stu_project.refreshFlag = true; }}else if (myKeyDown(VK_RIGHT))		// 右键{if (hero.move(TANK_DIRECTION_RIGHT)) { stu_project.refreshFlag = true; }}// 其他键不受限制if (myKeyDown(VK_SPACE))			// 空格{hero.shoot();stu_project.refreshFlag = true;}// 刷新界面if (stu_project.refreshFlag) { paint(); }
}// 鼠标监控
void mouseEvent()
{ExMessage msg;							// 鼠标消息结构体if (peekmessage(&msg, EM_MOUSE))		// 只监听鼠标消息{switch (msg.message){case WM_LBUTTONUP:				// 鼠标左键释放(用msg.x与msg.y来判断点击位置)break;case WM_MOUSEMOVE:				// 鼠标移动break;case WM_RBUTTONUP:				// 鼠标右键释放break;}}
}// 显示本关分数
void showScore()
{putimage(0, 0, &stu_project.imgScore);settextstyle(24, 0, "微软雅黑");settextcolor(YELLOW);setbkmode(TRANSPARENT);myDrawString(330, 58, to_string(stu_project.level + 1));settextcolor(WHITE);int score = 0;for (int i = 0; i < 4; i++){score = score + stu_project.enemyArmorDeadNum[i] * 100 * (i + 1);myDrawString(176, 183 + i * 42 , to_string(stu_project.enemyArmorDeadNum[i]));myDrawString(220, 183 + i * 42, "×");myDrawString(260, 183 + i * 42, to_string(100 * (i + 1)));myDrawString(320, 183 + i * 42, "=");myDrawString(350, 183 + i * 42, to_string(stu_project.enemyArmorDeadNum[i] * 100 * (i + 1)));Sleep(500);}settextcolor(RED);myDrawString(350, 350, to_string(score));Sleep(500);system("pause");
}// 处理游戏状态(下一关或失败)
void doGame()
{// 游戏结束if (stu_project.gameStatus == GAME_STATUS_OVER){myPlayAudio("./resources/audio/over.mp3");myDrawImage((GAME_WIDTH - stu_project.imgFailure.getwidth()) / 2, (GAME_HEIGHT - stu_project.imgFailure.getheight()) / 2, &stu_project.imgFailure);Sleep(1000);showScore();}else if (stu_project.gameStatus == GAME_STATUS_WIN){myDrawImage((GAME_WIDTH - stu_project.imgSuccess.getwidth()) / 2, (GAME_HEIGHT - stu_project.imgSuccess.getheight()) / 2, &stu_project.imgSuccess);Sleep(1000);showScore();stu_project.level++;if (stu_project.level < GameMap::getMaxLevel()){newGame();}}}// 开始游戏
void newGame()
{myPlayAudio("./resources/audio/begin.mp3");// 初始化游戏initGame();// 开始游戏while (stu_project.gameStatus == GAME_STATUS_RUN){// 开始刷新if (myTimer(INTERVAL_REFRESH, TIMERID_REFRESH)){// 绘制窗口paint();// 游戏进程run();}// 键盘监控keyEvent();// 鼠标监控(必须随时监控,否则不湿滑)//mouseEvent();}// 处理游戏状态(下一关或失败)doGame();
}

下载:

链接:https://pan.baidu.com/s/1Mk-yTrfrXnb-FmYO82YA4A?pwd=g67n 
提取码:g67n


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部