实现游戏地图读取与射击运行

射击代码来源自2D 横向对抗射击游戏(by STF) - CodeBus 

地图读取改装自 瓦片地图编辑器 解决边界检测,实现使用不同像素窗口也能移动不闪退-CSDN博客

 

 


// 程序:2D RPG 地图编辑器改游戏读取器 
// 作者:民用级脑的研发记录 射击部分代码移植自 :https://codebus.cn/contributor/stf-2d-shooting  
// 邮箱:1309602336@qq.com
// 编译环境:Devc++/VC 2010/Visual Studio 2022,EasyX_20220901/Easyx_2023 大暑版
// 编写日期:-2024-2-22 2024-4-14至2024-4-22
//
#undef UNICODE
#undef _UNICODE
#pragma warning(disable : 4996)		// VS2022 对某些函数警告,但是为了方便移植,就无视这些警告 这样 Devc++ VC2010 VS2022 都能跑
#include <graphics.h>
#include <stdio.h>
#include <string.h>
#include <io.h>
#include <direct.h>

void show_buffs();
#include <ctime>
void check_move();

void hp_bar();
void show_player();
void show_enemy();
void move_enemy();
//void draw_background();
int generate_line();					// 若返回 -1,表示生成线条失败

int create_p_b();						// 创建自机的子弹
int create_e_b();						// 创建敌机的子弹

int destroy_p_b(int index);
int destroy_e_b(int index);				// 删除一个子弹

#define FRAMERATE 20					// 画面刷新的周期(ms)
#define FIRERATE 350					// 射击间隔时间
#define E_FIRERATE 350					// 敌人射击间隔
#define BLEED_TIME 150					// 受伤闪烁时间

#define BACKGROUND 80					// 绘制背景线条的周期

#define MAX_LINES 75					// 最多同屏背景线条数目
#define MAX_PLAYER_BULLETS 40			// 最多同屏自机子弹数目
#define MAX_ENEMY_BULLETS 40			// 最多同屏敌机子弹数目


int player_pos[2] = { 30,30 };						// 自机位置xy
int enemy_bullet[MAX_ENEMY_BULLETS][2];				// 敌人的子弹位置
int player_bullet[MAX_PLAYER_BULLETS][2];			// 自机的子弹位置
int enemy_pos[2] = { 580,240 };						// 敌机位置
bool p_b_slots[MAX_PLAYER_BULLETS] = { false };		// 用于判断 player_bullet 的某个位置是否可用
bool e_b_slots[MAX_ENEMY_BULLETS] = { false };
int number_p_b = 0, number_e_b = 0;					// 记录自机和敌机的子弹数,减少遍历压力

int player_health = 100, enemy_health = 100;

bool isBleeding_p = false, isBleeding_e = false;	// 用于实现命中后的闪烁效果


int background_line[MAX_LINES][3];					// 背景的线条,三个参数分别是 x、y、长度
bool line_slots[MAX_LINES] = { false };
int number_lines = 0;								// 记录背景线条数目


clock_t begin_time = 0;

int buffs=0;
int canbuff=0;											// 用于控制 shift 减速时长
int leftshift=0;




// 以上是移植别人的射击游戏,已进行移动和蓝buff修改
//射击代码来源:https://codebus.cn/contributor/stf-2d-shooting 
// 射击游戏作者:STF(QQ:2292683261)
// * 编译环境:Visual Studio 2019,EasyX_20200315(beta)



//30.使用时发现像素putimage刷新界面未修改,pentable系列问题如瓦片重叠部分,30的瓦片放在20的网格导致的覆盖
// 2024.4.22 修改——以bkmeshcopywidth,替换边界,以bkhieght替换鼠标按键的if边界检测与视口大小,原始数据是 30像素正方形瓦片边长,2708270 的原始视口与 3*270宽高的缓存区 。如今已自适应,可以多种像素边长

// 从默认文件夹中读取自定义图片
void loadfilev2(char* DLC, IMAGE*** pentablev2);
// 加载自定义图片
void loadfile_scanf(char* dirname, char* next, char* kind, IMAGE** pentablev2);
// 游戏地图导入
void loadgamemapv2(int** map, int* gamemapi, int* gamemapj, IMAGE* pentable, IMAGE*** pentablev2, int* pixnum, int* imagenum);
// 实时渐变色小动画
inline void animationv2(int chararcterflag, int bkgameleft, int bkgametop, int bkmeshgameleft, int bkmeshgametop, int bkmeshdeskleft, int bkmeshdesktop);


// 从默认文件夹中读取自定义图片
void loadfile(IMAGE*** bentablev2,int pixnum);
// 加载自定义图片
void loadfile_scanf(char* dirname, char* next, char* kind, IMAGE** pentablev2,int pixnum);
// 保存自定义文件
void savepersonalfile(char* DLC, IMAGE*** pentablev2);
// 保存自定义瓦片贴图
void savelocal(char* DLC, char* dirname, char* next, char* kind, char* sign, IMAGE** files);
// 实时渐变色小动画
inline void animation(int chararcterflag, int bkgameleft, int bkgametop, int bkmeshgameleft, int bkmeshgametop, int bkmeshdeskleft, int bkmeshdesktop);
// 边界检查,move 是平移大小,单位:像素,若使得寄存区在九宫格中心,move 的大小就是寄存区的边长,相应的 size 的大小是 move 的两倍。这样无论长宽都是 3 倍。size 是大小区域,代表不会触发缓冲区更新的范围,单位:像素
inline void checkboundary(int* bkgameleft, int* bkgametop, int gamelimitright, int gamelimitbuttom, int* bkmeshleft, int* bkmeshtop, int* meshlimit, int* meshlimitbutton, int bkmeshcopywidth, int bkmeshcopyheight);
// 保存游戏地图贴图数据
void savegamemap(int** map, int gamemapi, int gamemapj, IMAGE* pentable, IMAGE*** pentablev2, int pixnum, int imagenum);
// 导入游戏地图贴图数据
void loadgamemap(int** map, int* gamemapi, int* gamemapj, IMAGE* pentable, int* pixnum, int* imagenum);
// 使用关键字 inline 声明为内联函数,减少贴图函数频繁调用的开销导致的卡顿。
// 缓冲区纹理映射函数:bkmesh 映射目标,map 映射总网格,pentable:纹理集,bkmeshmapi,bkmeshmapj:映射起始点,tilenum:横,纵映射的数量,pixnum:一个映射块的边长,单位:像素。defaultnum,默认未绘制网格的标号,defaultcolor 未绘制网格的颜色
inline void freshmesh(int** oldmesh, IMAGE* bkmesh,  int** map, IMAGE* pentable, IMAGE*** pentablev2, int bkmeshmapi, int bkmeshmapj, int bkmeshfreshi,int bkmeshfreshj,int pixnum,int defaultnum,int defaultcolor)
{
	int kind = 0;											// 存储代号第一位:类型
	int number = 0;											// 存储代号第二位:序列号
	int pennumber = -1;										// 暂存每一次循环的映射代号
	IMAGE* pen = NULL;										// 所找到的纹理
	int left = 0;											// 这是每次循环所找到的纹理对应映射地址
	int top = 0;
	SetWorkingImage(bkmesh);								// 设置绘图目标为游戏背景采样区,刷新采样区,刷新寄存区
//	cleardevice();											// 当时没注意导致一直重绘制,并且当时原版cleardevice失效,数组功能不确定。却由此能跑项目,可知当时问题根源不定导致的修改困难
	int sidei=bkmeshmapi + bkmeshfreshi;					// 计算变量,每次循环就不用再次计算了,表达式在for循环中会一直重复计算,用side替换for的表达式
	int sidej=bkmeshmapj + bkmeshfreshj;

	for (int i = bkmeshmapi; i <sidei; i++)
	{
		left = 0;
		for (int j = bkmeshmapj; j < sidej; j++)
		{
			// 注意数组越界
			pennumber = map[i][j];						// 读取游戏大地图数组序号
			if(oldmesh[i-bkmeshmapi][j-bkmeshmapj]==pennumber&&pennumber==defaultnum)							// 性能优化,如果相同位置瓦片序号相同,就不用重新绘制了,即缓存区只选择部分像素重新绘制
			{
				// 2024.4.22 重整,增加pennumber==defaultnum,只对空的瓦片不绘制,因为之后有的瓦片绘制一次会有残留直线边界,所以需要重绘解决
			}
			else
			{
				oldmesh[i-bkmeshmapi][j-bkmeshmapj]=pennumber;
				if (pennumber == defaultnum)
				{
					setfillcolor(BGR(defaultcolor));
					fillrectangle(left, top, left + pixnum, top + pixnum);				// 修正,如果是rectangle则会有黑色不完全打印
				}
				else
				{
					kind = pennumber / 10 - 6;						// 剥离第一位
					number = pennumber % 10;						// 剥离最后一位
					if (pennumber < 10)
						pen = &pentable[pennumber];
					else
						pen = pentablev2[kind][number];				// 根据序号查找对应贴图
					putimage(left, top, pen);						// 把贴图画到采样区
				}
			}
			left += pixnum;										// 往右移动,准备下一次绘制位置,此处贴图就会覆盖白色边框。为保证坐标变换和网格对应,算上网格宽度,也在贴图矩形内
		}
		top += pixnum;											// 往下移动,准备下一次绘制位置,此处就会覆盖白色边框,方便定位
	}
	SetWorkingImage();
}
// 修改为常数即只显示窗口大小的图像
inline void freshbk(IMAGE* bk, IMAGE* bkmesh, int gamex, int gamey, int bkmeshmapi, int bkmeshmapj, int bkwidth,int bkheight, int pixnum)
{
	SetWorkingImage(bkmesh);
	getimage(bk, gamex - bkmeshmapj * pixnum, gamey - bkmeshmapi * pixnum, bkwidth, bkheight);		// 计算去除平移多个bkmeshmapj对应的像素距离,从刚刚绘制好的采样区取样,刷新游戏背景寄存区。
	SetWorkingImage();
}
// 在屏幕显示截图
inline void showbk(IMAGE* bk, int bkdeskx, int bkdesky)
{
	SetWorkingImage();
	putimage(bkdeskx, bkdesky, bk);
}
// 在屏幕上显示缓冲区
inline void showbkmesh(IMAGE* bkmesh, int bkmeshdeskx, int bkmeshdesky)
{
	SetWorkingImage();
	putimage(bkmeshdeskx, bkmeshdesky, bkmesh);
}
// 初始化游戏地图
int** initmap(int wide, int high,int defaultnum)
{
	int** map = new int* [high];					// 二维数组动态初始化,先给二级指针挂上一个长度为 10 的指针数组
	for (int i = 0; i < high; i++)
	{
		map[i] = new int[wide];						// 然后数组里的每个指针都挂上一个长度为 10 的 int 类型数组
	}
	for (int i = 0; i < high; i++)
	{
		for (int j = 0; j < wide; j++)
		{
			map[i][j] = defaultnum;							// 初始化游戏大地图 map 的参数,参数 1 默认黑色
		}
	}
	return map;
}
int main()
{
	initgraph(1640, 980, 1);
	setbkcolor(GREEN);
	setlinecolor(WHITE);
	cleardevice();
	// 这里是当时重整的起点,复制粘贴这部分以前的变量去改写 ,写一块新的就注释一块旧的
	int defaultnum;								// 地图初始化时,默认空地图的序号
	defaultnum=9999;
	int defaultcolor=0x00CCCCCC;				// 00 是完全不透明,FF是完全透明00-FF刚好是0-255。 ARGB 0x 00: alpha + 00: red + 00: green + 00: blue ,但是绘制时 setfillcolor的格式是 BGR,所以使用时需要 BGR()转换。
	int pixnum;
	pixnum = 30;
//	pixnum = 60;
//	pixnum=15;
	// 规定上限参数
	// 进行初始化,规定各位置具体数字
	int bkmeshMaxwidth;							// 缓冲区自适应宽度,用于不同pixnum 下缓存九宫格的边界适应
	int bkmeshMaxheight;						// 缓冲区自适应高度,用于不同pixnum 下缓存九宫格的边界适应
	int mapMaxwidth;							// 游戏地图自适应高度网格数,用于选取最接近上限的网格数,用于不同的 pixnum 下游戏网格数目是九宫格的每一格的整数倍
	int mapMaxheight;
	int bkmeshcopyMaxheight;					// 自适应九宫格其中的一格格子的宽高上限。
	int bkmeshcopyMaxwidth;
	int bkMaxwidth;								// 用于限制窗口大小 ,这是在窗口可以拖住拉伸之后用的变量
	int bkMaxheight;							// 用于限制窗口大小 ,这是在窗口可以拖住拉伸之后用的变量
	// 上限的一种参考
	bkmeshcopyMaxheight=1000;					// 测试当宫格小于游戏视口时,绘制的边界检测
	bkmeshcopyMaxwidth=1000;
//	bkmeshcopyMaxheight=300;					// 测试数据,用于测试视口比单个宫格大的情况——出现黑边
//	bkmeshcopyMaxwidth=300;
//	bkmeshcopyMaxheight=900;
//	bkmeshcopyMaxwidth=1460;
	bkMaxheight=bkmeshcopyMaxheight;
	bkMaxwidth=bkmeshcopyMaxwidth;
	bkmeshMaxwidth=bkmeshcopyMaxwidth*3;						// 宽的上限界限是 九宫格其中一格宽的三倍像素
	bkmeshMaxheight=bkmeshcopyMaxheight*3;						// 高的上限界限是 九宫格其中一格高的三倍像素
//	mapMaxheight=bkmeshMaxheight*100;								// 游戏地图高度的像素上限是九宫格的高度的300倍
//	mapMaxwidth=bkmeshMaxwidth*100;								// 游戏地图宽度的像素上限是九宫格的宽度的300倍
//	mapMaxheight=bkmeshMaxheight*1;								// 测试发现边界闪退,越界数组刷新发现。游戏地图高度的像素上限是九宫格的高度的1倍
//	mapMaxwidth=bkmeshMaxwidth*1;								// 游戏地图宽度的像素上限是九宫格的宽度的1倍
	mapMaxheight=bkmeshMaxheight*2;								// 测试发现边界闪退,越界数组刷新发现。游戏地图高度的像素上限是九宫格的高度的2倍
	mapMaxwidth=bkmeshMaxwidth*2;
	// 实际自适应于瓦片的大小,用于保证不同的 background bk 都能在九宫格的每个宫格边缘切换地图时不错位。
	int bkheight;												// 2024.4.21修改,发现需要适配屏幕高度,增大面积,新增变量替换常数 270 ,先原样替换,验证替换正常,再继续改数据
	int bkwidth;
//	bkheight=370;
//	bkwidth=370;
	bkheight=470;												// 如果小于一个宫格的最大
	bkwidth=470;
	if(bkMaxheight<bkheight||bkMaxwidth<bkwidth)
	{
		printf("测试阶段:视口比单个宫格大,会在边界出现黑边情况\n");
	}
	else
	{
		printf("正常运行,视口比单个宫格小,不会在边界出现黑边情况\n");
	}

	IMAGE* bk;										// 背景图片寄存区
	bk = new IMAGE(bkwidth,bkheight);
	int bkmeshheight;
	int bkmeshwidth;
	int bkmeshcopywidth;							// 2024.4.21修改,增加copywidth,发现需要适配屏幕高度,增大面积,新增变量替换常数 270 ,先原样替换,验证替换正常,再继续改数据
	int bkmeshcopyheight;							// 九宫格的其中一格高度,采样越过九宫格左上四个块的边界,就进行更新九宫格,从原来的九宫格右边边界变成左边固定边界,实现不同大小的游戏背景 bk 不会因为边界检测距离变化而导致需要重新计算
	int bkmeshcopyi;								// 九宫格上下平移一次的网格个数,就是一格宫格竖着数有几个瓦片
	int bkmeshcopyj;								// 九宫格左右平移一次的网格个数,就是一格宫格横着数有几个瓦片
	int** map;
	int mapi;										// 读取 DLC 文件,需要变量记录循环次数,单位 瓦片贴图个数
	int mapj;
	int safemapi;									// 一个安全的贴图范围,避免阅读超过 10000 导致的越界闪退问题。
	int safemapj;
	int safemapright;								// 游戏右边界像素坐标,左边界坐标默认是0
	int safemapbottom;								// 游戏下边界像素坐标,上边界坐标默认是0
	int gamelimitright;								// 用于减少比较时的计算,存储游戏大地图边界范围
	int gamelimitbottom;
	int meshlimitright;								// 使用变量暂存边界,不用每次来回计算边界
	int meshlimitbottom;
	int bkmeshfreshi;								// 九宫格高有多少个瓦片,用于刷新缓存区时确定游戏网格宽的采样次数
	int bkmeshfreshj; 								// 九宫格宽有多少个瓦片,用于刷新缓存区时确定游戏网格高的采样次数
	// 开始自适应,各个存储区大小以及边界
	bkmeshcopyi= bkmeshcopyMaxheight/pixnum;		// 一格高度能按瓦片高度分,能分成几份,就是一格宫格横着数有几个瓦片
	bkmeshcopyj=bkmeshcopyMaxheight/pixnum;			// 一格宽度能按瓦片宽度分,能分成几份,就是一格宫格竖着数有几个瓦片
	bkmeshcopyheight=bkmeshcopyi*pixnum;			// 整除之后乘像素算出实际一格像素宽度,用于计算九宫格实际宽度
	bkmeshcopywidth=bkmeshcopyj*pixnum;
	bkmeshheight=bkmeshcopyheight*3;				// 九宫格的像素高度
	bkmeshwidth=bkmeshcopywidth*3;					// 九宫格的像素宽度
	bkmeshfreshi=bkmeshheight/pixnum;				// 计算九宫格横着一行有几个瓦片
	bkmeshfreshj=bkmeshwidth/pixnum;				// 计算九宫格竖着一列有几个瓦片
	meshlimitright=bkmeshcopywidth*2;				// 计算 bk 采样到右边界时更新所需的边界,是像素坐标,checkboundary 会更新这个边界
	meshlimitbottom=bkmeshcopyheight*2;				// 计算 bk 采样到下边界时更新所需的边界,是像素坐标,checkboundary 会更新这个边界
	mapi=mapMaxheight/bkmeshcopyheight*bkmeshcopyi;	// 计算实际游戏地图长有多少个瓦片,就是游戏最大高度整除瓦片高度,算出有几个宫格,然后在乘一个宫格竖着数瓦片个数
	mapj=mapMaxwidth/bkmeshcopywidth*bkmeshcopyj;	// 计算实际游戏地图宽有多少个瓦片,就是游戏最大宽度整除瓦片宽度,算出有几个宫格,然后在乘一个宫格横着数瓦片个数
//	safemapi=mapi-bkmeshcopyi*2;	// 测试倒数第一个九宫格左上角坐标		// 适配旧代码的同名变量,旧代码测试出来数据的位置被标记为safemapi,根据 地图和缓存区一样大时,越界检测,九宫格的最后一格宫格是地图边界时对应左上角,差两个宫格此时到边境了,不能再往右刷新。
//	safemapj=mapj-bkmeshcopyj*2;	// 测试倒数第一个九宫格左上角坐标		// 适配旧代码的同名变量,旧代码测试出来数据的位置被标记为safemapj,根据 地图和缓存区一样大时,越界检测,九宫格的最后一格宫格是地图边界时对应左上角,差两个宫格此时到边境了,不能再往下刷新。
	safemapi=mapi-1;										// 绘制的时候确定下网格界限,数据-1是因为数组的第 0 个对应左边第一个瓦片,于是等同于数组下标是总数-1 ,初始化n个,数组对应序号 0~n-1
	safemapj=mapj-1;										// 绘制的时候确定右网格边界,数据-1是因为数组的第 0 个对应左边第一个瓦片,于是等同于数组下标是总数-1 ,初始化n个,数组对应序号 0~n-1
	safemapbottom=mapi*pixnum-bkmeshcopyheight;			// 计算出采样区距离最后一格的像素距离,因为采样区的移动控制缓存区的移动,这样的设计导致只有采样区停止移动,缓存区才能停止移动,就需要把缓存区停止移动的位置换算成采样区停止移动的距离
	safemapright=mapj*pixnum-bkmeshcopywidth;			// 计算出采样区距离最后一格的像素距离,因为采样区的移动控制缓存区的移动,这样的设计导致只有采样区停止移动,缓存区才能停止移动,就需要把缓存区停止移动的位置换算成采样区停止移动的距离
	gamelimitright=safemapright;					// 换算出对应像素位置,就是玩家视口左上角坐标的极限,同样说明视口的最大不超过宫格,但实际上可以,只不过就是看到黑边但不闪退
	gamelimitbottom=safemapbottom;
	printf("mapj,i=%d,%d\n",mapj,mapi);
	printf("safemapj,safemapi %d,%d\n",safemapj,safemapi);
	printf("gamelimitrx,gamelimitry %d %d",gamelimitright,gamelimitbottom);

	map = initmap(mapj, mapi,defaultnum);			// 初始化游戏网格大地图!			j对应函数第一个参数,i对应第二个参数,否则会闪退
	IMAGE* bkmesh;									// 背景图片采样区
	bkmesh = new IMAGE(bkmeshwidth, bkmeshheight);
	int bkgameleft;								// 背景图片寄存区左上角坐标,是在游戏里的像素坐标。(0,0)可以理解为游戏大地图的左上角顶点。
	int bkgametop;
	int bkmeshgameleft;							// 背景图片采样区左上角坐标,是在游戏里的像素坐标。
	int bkmeshgametop;
	int bkmeshmapi;								// 背景图片采样区左上角所对应的 map 数组序号。从 map[0][0]开始,按照 map[i][j],其中 bkmeshmapi=bkmeshtop/pixnum
	int bkmeshmapj;
	bkgameleft = 0;								// 由于 bkgame 控制 mesh 坐标移动,(0,0)则游戏背景完全在当前采样区移动
	bkgametop = 0;
	bkmeshgameleft = 0;
	bkmeshgametop = 0;
	bkmeshmapi = bkmeshgametop / pixnum;				// 缓存区左上角所在的游戏网格的哪一行
	bkmeshmapj = bkmeshgameleft / pixnum;				// 缓存区左上角所在的游戏网格的哪一列
	int bkdeskleft;								// 规定在屏幕上显示游戏背景寄存区,此处记录其左上角在屏幕上的像素坐标
	int bkdesktop;
	int bkmeshdeskleft;							// 规定在屏幕上显示游戏背景采样区,此处记录其左上角在屏幕上的像素坐标
	int bkmeshdesktop;
	bkdeskleft = 200;							// 游戏背景左上角将会在屏幕的(200,200) 处
	bkdesktop = 200;
	bkmeshdeskleft = 700;						// 游戏背景缓冲区左上角将会在屏幕的(700,0)处
	bkmeshdesktop = 0;

	// 自此为止就是变量自适应结束

	int** oldmesh;
	oldmesh=new int*[bkmeshfreshi];
	for(int i=0; i<bkmeshfreshi; i++)
	{
		oldmesh[i] =new int[bkmeshfreshj];
		for(int j=0; j<bkmeshfreshj; j++)
		{
			oldmesh[i][j]=999999;						// 正数
		}
	}

	// 提出来的性能优化代码,用于freshmesh时刷新过滤相同的数字的瓦片,这样数字相同,瓦片相同,就不用绘制了

	int pentableleft;								// 忘了初始化调色盘了,这里设置调色盘左上角在屏幕的坐标
	int pentabletop;
	int imagenum;									// 调色板数量上限,用于文件读取控制循环次数
	IMAGE* pentable;								// 调色板其实就是贴图数组
	pentableleft = 0;								// 调色盘左上角将会在屏幕的(0,0)处
	pentabletop = 0;
	imagenum = 10;
	int pentablev2left;								// 自定义贴图插槽
	int pentablev2top;
	int pentablev2high;								// 自定义贴图的容量大小
	int pentablev2wide;
	pentablev2left = 10;
	pentablev2top = 300;
	pentablev2high = 10;
	pentablev2wide = 4;
	IMAGE*** pentablev2;
	pentablev2 = new IMAGE **[pentablev2wide];
	for (int i = 0; i < pentablev2wide; i++)
		pentablev2[i] = new IMAGE * [pentablev2high];
	for (int i = 0; i < pentablev2wide; i++)
		for (int j = 0; j < pentablev2high; j++)
			pentablev2[i][j] = NULL;
//	loadfile(pentablev2,pixnum);							// 加载本地图片
// 调色盘加载数据,需要调整,可以加载任意大小的图片,然后切割成多个小图片然后导出。
// 绘制的时候,可以选中多个瓦片,一次绘制多个瓦片,mesh除以选中的网格数。画区分线,每四个画一个网格线。
// 瓦片设计的时候,需要一个再绘图板实现数组刷新。原图放缩小图笔刷
	pentable = new IMAGE[imagenum];
	for (int i = 0; i < imagenum; i++)
	{
		pentable[i] = IMAGE(pixnum,pixnum);				// 默认调色板也自适应像素高度
		SetWorkingImage(&pentable[i]);					// 给调色板绘制颜色
		setfillcolor(RGB(i * 20, i * 20, i * 20));		// 这里初始化调色盘的颜色
		fillrectangle(-1, -1, pixnum, pixnum);				// 在调色板上绘制颜色(纹理)要从 -1,-1 开始绘制,把边框画到外部,不保留边框。
	}

	// 读取DLC
	loadgamemapv2(map, &safemapi, &safemapj, pentable, pentablev2, &pixnum, &imagenum);

	int left;												// 初始化绘制采样区所需的坐标,相对于采样区,(0,0)就是采样区左上角顶点坐标
	int top;
	left = 0;
	top = 0;
	//	往缓冲区刷入贴图
	SetWorkingImage(bkmesh);								// 设置绘图目标为游戏背景采样区,刷新采样区,刷新寄存区
	setbkcolor(RGB(200, 200, 200));							// 设置瓦片边线颜色,图片没有覆盖的背景就是边线
	cleardevice();
	for (int i = bkmeshmapi; i < bkmeshmapi + bkmeshfreshi; i++)
	{
		left = 0;
		for (int j = bkmeshmapj; j < bkmeshmapj + bkmeshfreshj; j++)
		{
			int pennumber = map[i][j];							// 读取游戏大地图数组序号
			if (pennumber == 9999)
			{
				rectangle(left, top, left + pixnum, top + pixnum);
			}
			left += pixnum;										// 往右移动,准备下一次绘制位置,
		}
		top += pixnum;											// 往下移动,准备下一次绘制位置
	}
	getimage(bk, bkgameleft, bkgametop, bkwidth,bkheight);				// 从刚刚绘制好的采样区取样,刷新游戏背景寄存区。
	//	开始往屏幕上绘图
	SetWorkingImage();															// 设置电脑屏幕为绘制对象
	for (int j = 0; j < 10; j++)
	{
		putimage(pentableleft + 10, pentabletop + j * pixnum, &pentable[j]); 		// 绘制绘图板
	}
	for (int i = 0; i < pentablev2wide; i++)									// 绘制自定义贴图,印自定义笔刷
		for (int j = 0; j < pentablev2high; j++)
			if (pentablev2[i][j] != NULL)
				putimage(pentablev2left + i * pixnum, pentablev2top + j * pixnum, pentablev2[i][j]);						// pixnum 替换 30
	putimage(bkdeskleft, bkdesktop, bk);										// 绘制游戏背景
	putimage(bkmeshdeskleft, bkmeshdesktop, bkmesh); 							// 显示游戏背景缓冲区
	// 此时绘制完成,以上 刷贴图,采样,粘贴就是实现 RPG 游戏大地图的压缩
	//	开始检测鼠标键盘功能
	int drawflag;															// 设置长按 flag
	int drawoldmx;															// 记录上一次绘制时的鼠标坐标,用于检测是否重复点击相同像素,来减少重复绘制
	int drawoldmy;
	int drawx;																// 画笔在游戏里的位置,单位像素
	int drawy;
	int olddrawi;															// 记录上一次绘制的瓦片,判断是否需要重新绘制
	int olddrawj;
	int drawsmallflag;														// 在 drawflag=1 时,检测是否刷新
	drawsmallflag = 0;
	olddrawi = 0;
	olddrawj = 0;
	drawx = 0;
	drawy = 0;
	drawflag = 0;
	drawoldmx = 0;
	drawoldmy = 0;
	int pentake;															// 设置不绘制时贴图代号为 -1
	pentake = 9;															// 默认白色笔刷,对应黑色背景
	int draftoldmx;															// 记录刚刚拖拽时的鼠标的位置,用于坐标变换计算位移
	int draftoldmy;
	int draftoldgamex;														// 记录刚刚拖拽时的游戏地图位置,用于坐标变换计算新的游戏坐标
	int draftoldgamey;
	int draftflag;															// 设置拖拽 flag
	draftoldmx = 0;
	draftoldmy = 0;
	draftoldgamex = 0;
	draftoldgamey = 0;
	draftflag = 0;
	int moveflag;															// 是否键盘控制移动
	int flag_x;																// 记录位移
	int flag_y;
	int speed;																// 键盘控制视口的移动速度
	speed = 5;
	flag_x = 0;
	flag_y = 0;
	moveflag = 0;
	int mousex;																// 记录鼠标位置
	int mousey;
	mousex = 0;
	mousey = 0;
	int oldbkmeshgamex;														// 判断拖拽时是否需要刷新 bkmesh 网格
	int oldbkmeshgamey;
	oldbkmeshgamex = -1;
	oldbkmeshgamey = -1;
	int i = -1;																// 用于暂存 mesh 网格坐标
	int j = -1;
	int characterflag;														// 用于按键时游戏小人的颜色变化
	characterflag = 5;
	ExMessage m;
	checkboundary(&bkgameleft, &bkgametop, gamelimitright, gamelimitbottom, &bkmeshgameleft, &bkmeshgametop, &meshlimitright, &meshlimitbottom, bkmeshcopyheight, bkmeshcopywidth);			// move =pixnum * 9, size = 2*9* pinxum 2024.4.22 替换 bkmeshcopyheight
	bkmeshmapi = bkmeshgametop / pixnum;
	bkmeshmapj = bkmeshgameleft / pixnum;
	freshmesh(oldmesh, bkmesh,map, pentable, pentablev2, bkmeshmapi, bkmeshmapj, bkmeshfreshi,bkmeshfreshj, pixnum,defaultnum, defaultcolor);			// 刷新,重新映射,其实就是开头初始化的代码,这里是给了一个封装示例,用了一个数组过滤重复位置进行性能优化-2024.4.21
	freshbk(bk, bkmesh, bkgameleft, bkgametop, bkmeshmapi, bkmeshmapj, bkwidth, bkheight, pixnum);
	showbkmesh(bkmesh, bkmeshdeskleft, bkmeshdesktop);
	showbk(bk, bkdeskleft, bkdesktop);
	int action=0;													// 玩家动作输入

	srand((unsigned)time(NULL));

	settextcolor(RGB(0, 254, 0));
	settextstyle(30, 0, "微软雅黑");
	outtextxy(50+100, 200-100, "AWSD 键移动, K 攻击, 右 Shift 切换加速模式");

	bool win = false, dead = false;

	clock_t firerate = clock();						// 射击控制
	clock_t e_firerate = clock();					// 控制敌机的射击
	clock_t runtime = clock();						// 用于控制画面刷新频率
	clock_t bleed_p = clock(), bleed_e = clock();	// 用于实现受伤闪烁
	clock_t backgroundline_generate = clock();		// 用于生成背景线条
	Sleep(3000);
//	BeginBatchDraw();

	bool leftshift = false;

	begin_time = clock();

	BeginBatchDraw();														// 开启双缓冲绘制,避免卡顿
	while (1)
	{
		while (peekmessage(&m, EX_KEY | EX_MOUSE))		// 一次性处理完鼠标消息,参考自 https://codebus.cn/zhaoh/handle-mouse-messages-correctly
		{
			switch (m.message)
			{
				case WM_LBUTTONDOWN:																				// 鼠标左键按下,有两种情况,一是选择贴图,另外就是绘制贴图
					if (drawflag == 0 && m.x > bkdeskleft && m.y > bkdesktop && m.x < bkdeskleft + bkwidth && m.y < bkdesktop + bkheight)		// 如果之前不是长按状态	且按下左键时,鼠标在游戏背景区域内
					{
						drawflag = 1;																				// 记录为正在绘制的状态
						mousex = m.x;																				// 记录坐标用于绘制
						mousey = m.y;
					}
					else if (drawflag == 0 && draftflag == 0 && m.x > 0 && m.y > 10 && m.x < 40 && m.y < 300)
					{
						pentake = m.y / pixnum;																			// 选择贴图对应的代号
						printf("pentake: %d\n", pentake);
					}
					else if (drawflag == 0 && draftflag == 0 && m.x > 10 && m.y > 300 && m.x < 10 + pentablev2wide * 30 && m.y < 300 + pentablev2high * 30)
					{
						if (pentablev2[(m.x - pentablev2left) / pixnum][(m.y - pentablev2top) / pixnum] != NULL)
							pentake = ((m.x - pentablev2left) / pixnum + 6) * 10 + (m.y - pentablev2top) / pixnum;	// 计算 map 的二位数代号
						printf("pentakev2: %d\n", pentake);
					}
					break;
				case WM_LBUTTONUP:
					drawflag = 0;
					drawsmallflag = 0;
					olddrawi = -1;
					olddrawj = -1;
					drawoldmx = -1;																					// 清除坐标记录,保证下次按键一定绘制
					drawoldmy = -1;
					break;
				case WM_RBUTTONDOWN:																				// 鼠标右键拖动
					if (draftflag == 0 && m.x > bkdeskleft && m.y > bkdesktop && m.x < bkdeskleft + bkwidth && m.y < bkdesktop + bkheight)
					{
						draftflag = 1;
						draftoldmx = m.x;																			// 记录鼠标坐标
						draftoldmy = m.y;
						mousex = m.x;
						mousey = m.y;
						draftoldgamex = bkgameleft;																	// 记录游戏背景寄存区左上角坐标
						draftoldgamey = bkgametop;
					}
					break;
				case WM_RBUTTONUP:
					draftflag = 0;
					bkgameleft = draftoldgamex - (m.x - draftoldmx);												// bkgameleft - draftoldgamex =- (m.x - draftoldmx)
					bkgametop = draftoldgamey - (m.y - draftoldmy);													// bkgametop - draftoldgamey =- (m.y - draftoldmy)
					mousex = -1;																					// draft 和 draw 共用 mousex mousey
					mousey = -1;
					break;
				case WM_KEYDOWN:
					switch (m.vkcode)				// 键盘移动控制
					{
						case 0x41:						// A
							if (flag_x - speed > -10)	// 限制范围,减少内存读写
								flag_x -= speed;
							characterflag = 1;
							break;
						case 0x57:						// W
							if (flag_y - speed > -10)
								flag_y -= speed;
							characterflag = 2;
							break;
						case 0x44:						// D
							if (flag_x + speed < 10)
								flag_x += speed;
							characterflag = 3;
							break;
						case 0x53:						// S
							moveflag = 1;
							if (flag_y + speed < 10)
								flag_y += speed;
							characterflag = 4;
							break;
						case VK_F1:
							savegamemap(map, mapi, mapj, pentable, pentablev2, pixnum, imagenum);
							break;
//						case VK_F2:
//							loadgamemap(map, &mapi, &mapj, pentable, &pixnum, &imagenum);
//							gamelimitright = pixnum * safemapi;
//							gamelimitbottom = pixnum * safemapj;
//							for (int i = 0; i < imagenum; i++)					// 刷新绘图板颜色
//							{
//								pentable[i] = IMAGE(pixnum, pixnum);
//								SetWorkingImage(&pentable[i]);					// 给调色板绘制颜色
//								setfillcolor(RGB(i * 20, i * 20, i * 20));		// 这里初始化调色盘的颜色
//								fillrectangle(-1, -1, pixnum, pixnum);			// 在调色板上绘制颜色(纹理)要从 -1,-1 开始绘制,把边框画到外部,不保留边框。
//							}
//							SetWorkingImage();
//							freshmesh(oldmesh,bkmesh, map, pentable, pentablev2, bkmeshmapi, bkmeshmapj, bkmeshfreshi,bkmeshfreshj, pixnum, defaultnum, defaultcolor);			// 刷新,重新映射,其实就是开头初始化的代码,这里是给了一个封装示例,用了一个数组过滤重复位置进行性能优化-2024.4.21
//							freshbk(bk, bkmesh, bkgameleft, bkgametop, bkmeshmapi, bkmeshmapj, bkwidth, bkheight, pixnum);								// 2024.4.21修改
//							showbk(bk, bkdeskleft, bkdesktop);
//							showbkmesh(bkmesh, bkmeshdeskleft, bkmeshdesktop);
//							break;
						case 0x4A:												// J键攻击
							action=1;
							break;
						case 0x55:											// U键攻击
							action=2;
							break;
					}
					break;
				case WM_KEYUP:
					switch (m.vkcode)
					{
						case 0x41:						// A
							flag_x = 0;
							break;
						case 0x57:						// W
							flag_y = 0;
							break;
						case 0x44:						// D
							flag_x = 0;
							break;
						case 0x53:						// S
							flag_y = 0;
							break;
					}
					if (flag_x == 0 && flag_y == 0)
						characterflag = 5;
					break;
				case WM_MOUSEMOVE:
					if (mousex != m.x || mousey != m.y)
					{
						mousex = m.x;
						mousey = m.y;
					}
					break;
			}
		}
		//		开始根据指令运行坐标变化
		if (draftflag == 1)
		{
			if (flag_x != 0)																	// 实现拖拽时键盘也能控制移动
				draftoldgamex += flag_x;
			if (flag_y != 0)
				draftoldgamey += flag_y;
			bkgameleft = draftoldgamex - (mousex - draftoldmx);									// bkgameleft-draftoldgamex=-(mousex-draftoldmx)
			bkgametop = draftoldgamey - (mousey - draftoldmy);									// bkgametop-draftoldgamey=-(mousey - draftoldmy)
			if (drawflag == 1 && mousex > bkdeskleft && mousey > bkdesktop && mousex < bkdeskleft + bkwidth && mousey < bkdesktop + bkheight)		// 实现边拖拽边移动边绘制
			{
				drawx = bkgameleft + (mousex - bkdeskleft);										// drawx-bkgameleft=m.x-bkdeskleft	横坐标方向移动距离相同
				drawy = bkgametop + (mousey - bkdesktop);										// drawy-bkgametop=m.y-bkdesktop	纵坐标方向移动距离相同
				i = drawy / pixnum;
				j = drawx / pixnum;
				if (olddrawi != i || olddrawj != j)
				{
					if (i > safemapi)															// map 数组越界检测
						i = safemapi;
					else if (i < 0)
						i = 0;
					if (j > safemapj)
						j = safemapj;
					else if (j < 0)
						j = 0;
					drawsmallflag = 1;
					map[i][j] = pentake;											// 注意 map[y][x],而不是 map[x][y],因为判断第几行,是通过 y 来控制上下移动的,判断第几列,是通过 x 左右移动的。
					olddrawi = i;
					olddrawj = j;
				}
				else
				{
					drawsmallflag = 0;												// 检测到是上一次绘制的瓦片,则不再刷新贴图与缓冲区。
				}
			}
		}
		else if (drawflag == 1 && flag_x == 0 && flag_y == 0 && (drawoldmx != mousex || drawoldmy != mousey) && mousex > bkdeskleft && mousey > bkdesktop && mousex < bkdeskleft + bkwidth && mousey < bkdesktop + bkheight)
		{
			// 注意不要越界,否则 gamex 为负数,导致数组越界闪退。
			// 通过实现坐标变换与赋值达到修改游戏大地图(数组)
			moveflag = 0;
			drawoldmx = mousex;
			drawoldmy = mousey;
			drawx = bkgameleft + (mousex - bkdeskleft);										// drawx-bkgameleft=mousex-bkdeskleft	横坐标方向移动距离相同
			drawy = bkgametop + (mousey - bkdesktop);										// drawy-bkgametop=mousey-bkdesktop	纵坐标方向移动距离相同
			i = drawy / pixnum;
			j = drawx / pixnum;

			if (olddrawi != i || olddrawj != j)
			{
				if (i > safemapi)															// map 数组越界检测,放内部则边界检测会多写,放外部则绘制时会多写消耗
					i = safemapi;
				else if (i < 0)
					i = 0;
				if (j > safemapj)
					j = safemapj;
				else if (j < 0)
					j = 0;
				drawsmallflag = 1;
				// 这里可以改成游戏里的按键提示区域
//				map[i][j] = pentake;														// 注意 map[y][x],而不是 map[x][y],因为判断第几行,是通过 y 来控制上下移动的,判断第几列,是通过 x 左右移动的。
				olddrawi = i;
				olddrawj = j;
			}
			else
			{
				drawsmallflag = 0;															// 检测到是上一次绘制的瓦片,则不再刷新贴图与缓冲区。
			}
		}	//	对绘制进行分类计算数据,剥离特殊情况的重复绘制,仅仅是 flag_x,或者 flag_y 不为零时取消重复绘制判断
		else if (drawflag == 1 && mousex > bkdeskleft && mousey > bkdesktop && mousex < bkdeskleft + bkwidth && mousey < bkdesktop + bkheight)
		{
			moveflag = 1;
			bkgameleft += flag_x;															// 更新游戏背景寄存区左上角坐标
			bkgametop += flag_y;
			drawx = bkgameleft + (mousex - bkdeskleft);										// drawx-bkgameleft=m.x-bkdeskleft	横坐标方向移动距离相同
			drawy = bkgametop + (mousey - bkdesktop);										// drawy-bkgametop=m.y-bkdesktop	纵坐标方向移动距离相同
			i = drawy / pixnum;
			j = drawx / pixnum;
			if (olddrawi != i || olddrawj != j)
			{
				if (i > safemapi)															// map 数组越界检测
					i = safemapi;
				else if (i < 0)
					i = 0;
				if (j > safemapj)
					j = safemapj;
				else if (j < 0)
					j = 0;
				printf("draw j,i=%d,%d\n",j,i);												// 测试数据
				printf("bkgamex,y %d %d\n",bkgameleft,bkgametop);
				printf("bkmeshmapi,bkmeshmapj %d %d\n",bkmeshmapi,bkmeshmapj);
				drawsmallflag = 1;
				// 这里可以改成游戏里的按键提示区域
//				map[i][j] = pentake;											// 注意 map[y][x],而不是 map[x][y],因为判断第几行,是通过 y 来控制上下移动的,判断第几列,是通过 x 左右移动的。
				olddrawi = i;
				olddrawj = j;
			}
			else
			{
				drawsmallflag = 0;												// 检测到是上一次绘制的瓦片,则不再刷新贴图与缓冲区。
			}
		}
		else if ((drawflag == 0 && flag_x != 0 )|| flag_y != 0)
		{
			moveflag = 1;
			bkgameleft += flag_x;
			bkgametop += flag_y;
		}
		else
		{
			// 既不绘制也不移动也不拖拽
		}
		// 根据计算出的坐标数据进行绘制,分多种情况分别绘制,减少函数重复调用与无效调用
		if (drawsmallflag == 1 && moveflag == 0)
		{
			freshmesh(oldmesh, bkmesh,map, pentable, pentablev2, bkmeshmapi, bkmeshmapj, bkmeshfreshi,bkmeshfreshj, pixnum,defaultnum, defaultcolor);			// 刷新,重新映射,其实就是开头初始化的代码,这里是给了一个封装示例,用了一个数组过滤重复位置进行性能优化-2024.4.21
			freshbk(bk, bkmesh, bkgameleft, bkgametop, bkmeshmapi, bkmeshmapj, bkwidth, bkheight, pixnum);
			showbk(bk, bkdeskleft, bkdesktop);
			showbkmesh(bkmesh, bkmeshdeskleft, bkmeshdesktop);
		}
		else if (moveflag == 1 && drawsmallflag == 1)
		{
			checkboundary(&bkgameleft, &bkgametop, gamelimitright, gamelimitbottom, &bkmeshgameleft, &bkmeshgametop, &meshlimitright, &meshlimitbottom, bkmeshcopyheight, bkmeshcopywidth);			// move =pixnum * 9, size = 2*9* pinxum 2024.4.22 替换 bkmeshcopyheight
			bkmeshmapi = bkmeshgametop / pixnum;
			bkmeshmapj = bkmeshgameleft / pixnum;
			freshmesh(oldmesh, bkmesh,map, pentable, pentablev2, bkmeshmapi, bkmeshmapj, bkmeshfreshi,bkmeshfreshj, pixnum,defaultnum, defaultcolor);			// 刷新,重新映射,其实就是开头初始化的代码,这里是给了一个封装示例,用了一个数组过滤重复位置进行性能优化-2024.4.21
			freshbk(bk, bkmesh, bkgameleft, bkgametop, bkmeshmapi, bkmeshmapj, bkwidth, bkheight, pixnum);
			showbkmesh(bkmesh, bkmeshdeskleft, bkmeshdesktop);
			showbk(bk, bkdeskleft, bkdesktop);
		}
		else if (moveflag == 1 || draftflag)															// 分类渲染, drawflag==0 时,再选择性刷新缓冲区
		{
			checkboundary(&bkgameleft, &bkgametop, gamelimitright, gamelimitbottom, &bkmeshgameleft, &bkmeshgametop, &meshlimitright, &meshlimitbottom,bkmeshcopyheight, bkmeshcopywidth);			// move =pixnum * 9, size = 2*9* pinxum
			bkmeshmapi = bkmeshgametop / pixnum;
			bkmeshmapj = bkmeshgameleft / pixnum;
			if (oldbkmeshgamex != bkmeshgameleft || oldbkmeshgamey != bkmeshgametop)					// 判断是否更新采样区
			{
				freshmesh(oldmesh,bkmesh, map, pentable, pentablev2, bkmeshmapi, bkmeshmapj, bkmeshfreshi,bkmeshfreshj, pixnum,defaultnum, defaultcolor);			// 刷新,重新映射,其实就是开头初始化的代码,这里是给了一个封装示例,用了一个数组过滤重复位置进行性能优化-2024.4.21
				oldbkmeshgamex = bkmeshgameleft;
				oldbkmeshgamey = bkmeshgametop;
				showbkmesh(bkmesh, bkmeshdeskleft, bkmeshdesktop);
			}
			freshbk(bk, bkmesh, bkgameleft, bkgametop, bkmeshmapi, bkmeshmapj, bkwidth, bkheight, pixnum);
			showbk(bk, bkdeskleft, bkdesktop);
		}
		animationv2(characterflag, bkgameleft, bkgametop, bkmeshgameleft, bkmeshgametop, bkmeshdeskleft, bkmeshdesktop);		// 小方块动画

		// 显示玩家
		fillrectangle(bkdeskleft+bkwidth/2-pixnum/2,bkdesktop+bkheight/2-pixnum/2,bkdeskleft+bkwidth/2+pixnum/2,bkdesktop+bkheight/2+pixnum/2);

		if(action==1)
		{
			SetWorkingImage(bk); 
			static int cnt=0;
			if(action==1&&cnt==0)
				cnt=1;
			if(cnt>=60&&action==1)
			{
				cnt=0;
				action=0;
			}
			else if(action==1)
			{

				for(int i=0; i<5; i++)
				{
					
					setfillcolor(RGB(cnt%255,cnt*2%255,cnt*9%255));
					solidcircle(bkwidth/2,bkheight/2,i+cnt);
					
				}
				cnt++;
			}
			printf("cnt %d\n",cnt);
			SetWorkingImage(); 
		}

		if (clock() - runtime >= FRAMERATE)
		{
			runtime = clock();
//			cleardevice();
//			draw_background();
			setfillcolor(GREEN);
			fillrectangle(0,650,685,940);
			show_buffs();
			hp_bar();// 画血条


			SetWorkingImage(bk);
			show_player();
			show_enemy();

			int n_p_b = 1, n_e_b = 1;					// 计数,遍历子弹,刷新位置
			int p_b_toprocess = number_p_b, e_b_toprocess = number_e_b;	// 需要处理的子弹数
			for (int i = 0; i < MAX_PLAYER_BULLETS && (n_p_b <= p_b_toprocess || n_e_b <= e_b_toprocess); ++i)
			{
				if (n_p_b <= p_b_toprocess)				// 如果子弹已经处理完就不处理了
				{
					if (p_b_slots[i] == true)
					{
						++n_p_b;
						player_bullet[i][0] += 3;
						setfillcolor(RGB(150, 180, 210));
						if (player_bullet[i][0] >= 635)
						{
							destroy_p_b(i);	// 到达了屏幕最右端
						}

						// 碰撞检测,两个矩形
						if ((player_bullet[i][0] + 5 >= enemy_pos[0] - 20 && player_bullet[i][0] - 5 <= enemy_pos[0] + 20) && (player_bullet[i][1] - 5 < enemy_pos[1] + 40 && player_bullet[i][1] + 5 > enemy_pos[1] - 40))
							// 击中敌人
						{
							destroy_p_b(i);
							enemy_health -= 8;
							isBleeding_e = true;
							bleed_e = clock();
						}

						fillrectangle(player_bullet[i][0] - 5, player_bullet[i][1] - 5, player_bullet[i][0] + 5, player_bullet[i][1] + 5);		// 画子弹
					}

				}

				if (n_e_b <= e_b_toprocess)				// 敌人的子弹
				{
					if (e_b_slots[i] == true)
					{
						++n_e_b;
						enemy_bullet[i][0] -= 3;
						setfillcolor(RGB(255, 180, 20));
						if (enemy_bullet[i][0] < 5)
						{
							destroy_e_b(i);
						}

						// 碰撞检测,两个矩形
						if (enemy_bullet[i][0] - 5 < player_pos[0] + 25 && enemy_bullet[i][0] + 5 > player_pos[0] - 25 && enemy_bullet[i][1] - 5 < player_pos[1] + 25 && enemy_bullet[i][1] + 5 > player_pos[1] - 25)
						{
							// 击中自机
							isBleeding_p = true;
							destroy_e_b(i);
							player_health -= 8;
							bleed_p = clock();
						}
						fillrectangle(enemy_bullet[i][0] - 5, enemy_bullet[i][1] - 5, enemy_bullet[i][0] + 5, enemy_bullet[i][1] + 5);
					}

				}
			}
			SetWorkingImage();
			showbk(bk, bkdeskleft, bkdesktop);
			if (win || dead)
				break;
			FlushBatchDraw();							// 在这里刷新才有效 不闪屏

			move_enemy();
			check_move();								// 替换移动 
			if (player_health <= 0)
				dead = true;
			if (enemy_health <= 0)
			{
				win = true;
			}

//			if (GetAsyncKeyState(VK_LSHIFT) & 0x8000)	// 按住 Shift 减速
//			{
//				leftshift = true;
//			}
//			else
//			{
//				leftshift = false;
//			}
//
//			if (GetAsyncKeyState(VK_UP) & 0x8000)
//				// 玩家移动
//			{
//				if (player_pos[1] >= 28)
//				{
//					if (leftshift)
//						player_pos[1] -= 2;				// y 的正方向是向下的
//					else
//						player_pos[1] -= 5;
//				}
//
//			}
			if (clock() - firerate >= FIRERATE && GetAsyncKeyState('K') & 0x8000)
				// 玩家开火
			{
				firerate = clock();
				create_p_b();
			}
//			if (GetAsyncKeyState(VK_DOWN) & 0x8000)
//				// 玩家移动
//			{
//				if (player_pos[1] <= 452)
//				{
//					if (leftshift)
//						player_pos[1] += 2;
//					else
//						player_pos[1] += 5;
//				}
//			}
//			if (GetAsyncKeyState(VK_LEFT) & 0x8000)
//				// 玩家移动
//			{
//				if (player_pos[0] >= 30)
//				{
//					if (leftshift)
//						player_pos[0] -= 2;
//					else
//						player_pos[0] -= 5;
//				}
//			}
//			if (GetAsyncKeyState(VK_RIGHT) & 0x8000)
//				// 玩家移动
//			{
//				if (player_pos[0] <= 320)
//					if (leftshift)
//						player_pos[0] += 2;
//					else
//						player_pos[0] += 5;
//			}


			if (clock() - e_firerate >= E_FIRERATE)
			{
				e_firerate = clock();
				create_e_b();
			}


			if (clock() - bleed_p >= BLEED_TIME)		// 受伤时间结束后关闭受伤闪烁效果
			{
				isBleeding_p = false;
			}

			if (clock() - bleed_e >= BLEED_TIME)		// 受伤时间结束后关闭受伤闪烁效果
			{
				isBleeding_e = false;
			}

			if (clock() - backgroundline_generate >= BACKGROUND)
			{
				backgroundline_generate = clock();
				generate_line();
			}
		}
//		else if(action==2)
//		{
//			static int cnt=0;
//			if(cnt==20)
//			{
//				cnt==0;
//				action=0;
//			}
//			else
//			{
//				cnt++;
//			}
//
//		}

//		animation(characterflag, bkgameleft, bkgametop, bkmeshgameleft, bkmeshgametop, bkmeshdeskleft, bkmeshdesktop);
//		FlushBatchDraw();																// 双缓冲批量绘制
		Sleep(2);																		// 休眠 2 毫秒,减少 CPU 占用
	}
	if (win)
	{
		settextcolor(RGB(0, 254, 0));
		settextstyle(35, 0,  "黑体");
		outtextxy(150, 200,  "你打败了boss!你赢了!!");
	}
	else
	{
		settextcolor(RGB(254, 0, 0));
		settextstyle(35, 0,  "黑体");
		outtextxy(140, 200,  "你被boss打败了!");
	}
	FlushBatchDraw();
	Sleep(5000);
	EndBatchDraw();
	return 0;
}


void hp_bar()
{
	setlinecolor(RGB(255, 255, 255));
	line(0, 481+200, 640, 481+200);										// 一条分割线
	settextstyle(20, 0,  "黑体");
//	settextstyle(20,0,"华文新魏");
	outtextxy(10, 485+200,  "BOSS的生命值:");
	outtextxy(10, 520+200,  "玩家的生命值:");
	setfillcolor(RGB(0, 255, 1));
	setlinecolor(WHITE);
	rectangle(160, 515+200, 560, 540+200);								// 血条外框
	setfillcolor(RGB(0, 255, 1));
	setlinecolor(RGB(255, 255, 255));
	if (player_health > 0)
		fillrectangle(160, 515+200, 160 + player_health * 4, 540+200);	// 玩家血条
	setlinecolor(WHITE);
	rectangle(160, 485+200, 560, 510+200);								// 敌人血条外框
	setfillcolor(RGB(230, 0, 1));
	setlinecolor(RGB(255, 255, 255));
	if (enemy_health > 0)
		fillrectangle(160, 485+200, 160 + enemy_health * 4, 510+200);	// 敌人血条

}

void show_buffs()
{
	setlinecolor(RGB(255,255,255));
	line(0,481+200,640,481+200);
	settextstyle(20,0,"黑体");
	outtextxy(10,620+200,"玩家的蓝条:");
	setfillcolor(RGB(255,0,255));
	rectangle(160,615+200,560,640+200);

	setfillcolor(RGB(0,255,255));
	setlinecolor(RGB(255,255,255));
	if(buffs>0)
		fillrectangle(160,615+200,160+buffs*2,640+200);

}

void check_move()
{
	if(canbuff==1&&buffs<=0)								// 单向限制,第一次倒退到 0 的时候,取消 shift 减速功能
		canbuff=0;
	else if(canbuff==0&&buffs>50)							// 当回复到 50 的时候,重启允许检测 shift 减速功能
		canbuff=1;
	if (canbuff==1&&GetAsyncKeyState(VK_RSHIFT) & 0x8000)	// 按住 Shift 减速
		leftshift = true;
	else
		leftshift = false;
	if(leftshift==true&&buffs>0)							// 消耗蓝条
		buffs-=2;
	else if(buffs<200)										// 恢复蓝条
		buffs+=1;
	if (GetAsyncKeyState(0x57) & 0x8000)
		// 玩家移动
		if (player_pos[1] >= 28)
			if (leftshift)
				player_pos[1] -= 12;							// y 的正方向是向下的
			else
				player_pos[1] -= 5;
	if (GetAsyncKeyState(0x53) & 0x8000)
		// 玩家移动
		if (player_pos[1] <= 452)
			if (leftshift)
				player_pos[1] += 12;
			else
				player_pos[1] += 5;
	if (GetAsyncKeyState(0x41) & 0x8000)
		// 玩家移动
		if (player_pos[0] >= 30)
			if (leftshift)
				player_pos[0] -= 12;
			else
				player_pos[0] -= 5;
	if (GetAsyncKeyState(0x44) & 0x8000)
		// 玩家移动
		if (player_pos[0] <= 320)
			if (leftshift)
				player_pos[0] += 12;
			else
				player_pos[0] += 5;
}

void show_player()
{
	if (isBleeding_p)
		setfillcolor(RGB(255, 0, 0));
	else
		setfillcolor(RGB(150, 180, 210));
	fillrectangle(player_pos[0] - 25, player_pos[1] - 25, player_pos[0] + 25, player_pos[1] + 25);
	setfillcolor(RGB(100, 200, 180));
	fillrectangle(player_pos[0], player_pos[1] + 5, player_pos[0] + 40, player_pos[1] - 5);
}

void show_enemy()
{
	if (isBleeding_e)
		setfillcolor(RGB(255, 0, 0));
	else
		setfillcolor(RGB(0, 130, 125));
	fillrectangle(enemy_pos[0] - 20, enemy_pos[1] - 40, enemy_pos[0] + 20, enemy_pos[1] + 40);
	setfillcolor(RGB(100, 200, 180));
	fillrectangle(enemy_pos[0], enemy_pos[1] + 5, enemy_pos[0] - 40, enemy_pos[1] - 5);
}

void move_enemy()
{
	static bool angle_v;		// 控制敌机的竖直移动方向,true 为向上,到边缘就换向
	static bool angle_h;		// 控制敌机的水平移动方向,true 为向左,到边缘就换向
	static clock_t interval;	// 定时随机换向

	if (clock() - interval >= 2000)
	{
		interval = clock();
		if (rand() % 2)			// 一般的概率换向
			angle_v = !angle_v;
		if (rand() % 2)
			angle_h = !angle_h;
	}

	if (angle_v == true)		// 到了地图边缘就调头
		enemy_pos[1] -= 3;
	else
		enemy_pos[1] += 3;
	if (angle_h == true)
		enemy_pos[0] -= 3;
	else
		enemy_pos[0] += 3;


	if (enemy_pos[1] >= 440)
		angle_v = true;
	else if (enemy_pos[1] <= 40)
		angle_v = false;


	if (enemy_pos[0] >= 580)
		angle_h = true;
	else if (enemy_pos[0] <= 380)
		angle_h = false;

}

//void draw_background()
//{
//	setlinecolor(WHITE);
//	int n_b_l = number_lines;		// 待处理线条数目
//	for (int i = 0; i < MAX_LINES && (n_b_l > 0); ++i)
//	{
//		if (line_slots[i] == true)
//		{
//			if (background_line[i][0] + background_line[i][2] <= 0)		// 说明线条出了屏幕
//			{
//				--number_lines;
//				line_slots[i] = false;
//			}
//			else
//			{
//				background_line[i][0] -= 10;				// 线条移动
//				line(background_line[i][0], background_line[i][1], background_line[i][0] + background_line[i][2], background_line[i][1]);
//			}
//			--n_b_l;
//		}
//
//	}
//}

int generate_line()
{
	if (number_lines >= MAX_LINES)
		return -1;
	++number_lines;
	for (int i = 0; i < MAX_LINES; ++i)
	{
		if (line_slots[i] == false)
		{
			line_slots[i] = true;
			background_line[i][0] = 640;				// 线条出现于屏幕最右边
			background_line[i][1] = rand() % 480;		// 线条高度随机
			background_line[i][2] = 10 + rand() % 50;	// 线条长度随机在 10-50 像素之间

			break;
		}
	}
	return 0;
}

int create_p_b()
{
	if (number_p_b > MAX_PLAYER_BULLETS)			// 空间不够
		return -1;
	for (int i = 0; i < MAX_PLAYER_BULLETS; ++i)	// 搜索 slots,寻找空位
	{
		if (p_b_slots[i] == false)
		{
			p_b_slots[i] = true;
			player_bullet[i][0] = player_pos[0] + 45;
			player_bullet[i][1] = player_pos[1];	// 创建子弹
			++number_p_b;
			break;
		}
	}
	return 0;
}

int create_e_b()
{
	if (number_e_b > MAX_ENEMY_BULLETS)				// 空间不够
		return -1;
	for (int i = 0; i < MAX_ENEMY_BULLETS; ++i)		// 搜索 slots,寻找空位
	{
		if (e_b_slots[i] == false)
		{
			e_b_slots[i] = true;
			enemy_bullet[i][0] = enemy_pos[0] - 45;
			enemy_bullet[i][1] = enemy_pos[1];		// 创建子弹
			++number_e_b;
			break;
		}
	}
	return 0;
}

int destroy_p_b(int index)
{
	if (index > MAX_PLAYER_BULLETS - 1)
		return -2;
	if (p_b_slots[index] == false)
		return -1;
	p_b_slots[index] = false;
	--number_p_b;
	return 0;
}

int destroy_e_b(int index)
{
	if (index > MAX_ENEMY_BULLETS - 1)
		return -2;
	if (e_b_slots[index] == false)
		return -1;
	e_b_slots[index] = false;
	--number_e_b;
	return 0;
}

// 保存游戏地图的全部数据
void savegamemap(int** map, int gamemapi, int gamemapj, IMAGE* pentable, IMAGE*** pentablev2, int pixnum, int imagenum)
{
	FILE* fp;
	int i = 0;
	int j = 0;
	char dirpath[400] = { '\0' };
	char filepath[400] = { '\0' };
	for (i = 0; i < 100; i++)
	{
		sprintf(dirpath, "DLC%d", i);
		if (access(dirpath, 0) == -1) 									// 检查 DLC 是否存在,不存在为 -1
		{
			break;
		}
	}
	mkdir(dirpath);														// 创建文件夹
	char filename[400] = "gamemap.txt";
	const char* next = "./";
	strcat(filepath, dirpath);
	strcat(filepath, next);
	strcat(filepath, filename);
	fp = fopen(filepath, "w+");											// 创建 .txt 文件
	fprintf(fp, "注意此导出的游戏文件为按键 F1 后自动生成 修改汉语字符或者英文字符 或导致该 .txt 文件不可读取\n");
	fprintf(fp, "warning this saved gamefile is automatically create after F1 pressed change Chinesse character or English character lead to read failed");
	fprintf(fp, "pixnum %d\n", pixnum);									// 正方形瓦片贴图边长信息:单位:像素
	fprintf(fp, "imagenum %d\n", imagenum);								// 瓦片个数
	char imageindix[400] = { '\0' };
	for (i = 0; i < imagenum; i++)
	{
		sprintf(imageindix, "tile_%d.png", i);
		fprintf(fp, "%s\n", imageindix);
	}
	fprintf(fp, "gamemapi %d gamemapj %d\n", gamemapi, gamemapj);
	for (i = 0; i < gamemapi; i++)
	{
		fprintf(fp, "%d", map[i][j]);
		for (j = 0; j < gamemapj; j++)
		{
			fprintf(fp, " %d", map[i][j]);
		}
		fprintf(fp, "\n");
	}
	fclose(fp);
	char imagename[400] = { '\0' };
	char imagepath[400] = { '\0' };
	for (i = 0; i < imagenum; i++)
	{
		sprintf(imagename, "tile_%d.png", i);							// 数字转字符串
		strcpy(imagepath, dirpath);										// 函数直接从头开始粘贴,自动清除开头的字符
		strcat(imagepath, next);
		strcat(imagepath, imagename);
		saveimage(imagepath, &pentable[i]);								// 批量导出贴图
	}
	savepersonalfile(dirpath, pentablev2);								// 创建默认文件夹并保存自定义贴图
}
 读取 DLC 继续开发
//void loadgamemap(int** map, int* gamemapi, int* gamemapj, IMAGE* pentable, int* pixnum, int* imagenum)
//{
//	FILE* fp;
//	int i = 0;
//	int j = 0;
//	char dirpath[400] = { '\0' };
//	char filepath[400] = { '\0' };
//	for (i = 0; i < 100; i++)
//	{
//		sprintf(dirpath, "DLC%d", i);
//		if (access(dirpath, 0) == 0) 										// 检查 DLC 是否存在,存在为 0
//		{
//			break;
//		}
//	}
//	if (i == 100)															// 100 次查找失败,则返回,不再读取
//		return;
//	char filename[400] = "gamemap.txt";
//	const char* next = "./";
//	strcat(filepath, dirpath);
//	strcat(filepath, next);
//	strcat(filepath, filename);
//	fp = fopen(filepath, "r");											// 读取 .txt 文件
//	fscanf(fp, "注意此导出的游戏文件为按键 F1 后自动生成 修改汉语字符或者英文字符 或导致该 .txt 文件不可读取\n");
//	fscanf(fp, "warning this saved gamefile is automatically create after F1 pressed change Chinesse character or English character lead to read failed");
//	fscanf(fp, "pixnum %d\n", pixnum);									// 正方形瓦片贴图边长信息:单位:像素
//	fscanf(fp, "imagenum %d\n", imagenum);								// 瓦片个数
//	char imageindix[400] = { '\0' };
//	char imagepath[400] = { '\0' };
//	for (i = 0; i < *imagenum; i++)
//	{
//		fscanf(fp, "%s\n", imageindix);
//		strcpy(imagepath, dirpath);
//		strcat(imagepath, next);
//		strcat(imagepath, imageindix);
//		loadimage(&pentable[i], imagepath, *pixnum, *pixnum, false);	// 批量导入贴图
//	}
//	fscanf(fp, "gamemapi %d gamemapj %d\n", gamemapi, gamemapj);
//	for (i = 0; i < *gamemapi; i++)
//	{
//		fscanf(fp, "%d", &map[i][j]);
//		for (j = 0; j < *gamemapj; j++)
//		{
//			fscanf(fp, " %d", &map[i][j]);
//		}
//		fscanf(fp, "\n");
//	}
//	fclose(fp);
//}

// 读取 DLC 继续开发
void loadgamemapv2(int** map, int* gamemapi, int* gamemapj, IMAGE* pentable, IMAGE*** pentablev2, int* pixnum, int* imagenum)
{
	FILE* fp;
	int i = 0;
	int j = 0;
	char dirpath[400] = { '\0' };
	char filepath[400] = { '\0' };
	for (i = 0; i < 100; i++)
	{
		sprintf(dirpath, "DLC%d", i);
		if (access(dirpath, 0) == 0) 										// 检查 DLC 是否存在,存在为 0
		{
			break;
		}
	}
	if (i == 100)															// 100 次查找失败,则返回,不再读取
		return;
	char filename[400] = "gamemap.txt";
	const char* next = "./";
	strcat(filepath, dirpath);
	strcat(filepath, next);
	strcat(filepath, filename);
	fp = fopen(filepath, "r");												// 读取 .txt 文件
	fscanf(fp, "注意此导出的游戏文件为按键 F1 后自动生成 修改汉语字符或者英文字符 或导致该 .txt 文件不可读取\n");
	fscanf(fp, "warning this saved gamefile is automatically create after F1 pressed change Chinesse character or English character lead to read failed");
	fscanf(fp, "pixnum %d\n", pixnum);										// 正方形瓦片贴图边长信息:单位:像素
	fscanf(fp, "imagenum %d\n", imagenum);									// 瓦片个数
	char imageindix[400] = { '\0' };
	char imagepath[400] = { '\0' };
	for (i = 0; i < *imagenum; i++)
	{
		fscanf(fp, "%s\n", imageindix);
		strcpy(imagepath, dirpath);
		strcat(imagepath, next);
		strcat(imagepath, imageindix);
		loadimage(&pentable[i], imagepath, *pixnum, *pixnum, false);		// 批量导入贴图
	}
	fscanf(fp, "gamemapi %d gamemapj %d\n", gamemapi, gamemapj);
	for (i = 0; i < *gamemapi; i++)
	{
		fscanf(fp, "%d", &map[i][j]);
		for (j = 0; j < *gamemapj; j++)
		{
			fscanf(fp, " %d", &map[i][j]);
		}
		fscanf(fp, "\n");
	}
	fclose(fp);
	printf("导入 map 部分正常\n");
	loadfilev2(dirpath, pentablev2);
}
// 从默认文件夹中读取自定义图片
void loadfilev2(char* DLC, IMAGE*** pentablev2)
{
	char brick[100] = "aa_birck_6_family";
	char ground[100] = "ab_ground_7_family";
	char change[100] = "ac_change_8_family";
	char trick[100] = "ad_trick_9_family";
	char next[100] = "./";
	char png[100] = "*.png";
	char dirpath[100] = {};
	strcpy(dirpath, DLC);
	strcat(dirpath, next);
	strcat(dirpath, brick);
	loadfile_scanf(dirpath, next, png, pentablev2[0]);								// 加载用于当砖头类型的图片
	strcpy(dirpath, DLC);
	strcat(dirpath, next);
	strcat(dirpath, ground);
	loadfile_scanf(dirpath, next, png, pentablev2[1]);
	strcpy(dirpath, DLC);
	strcat(dirpath, next);
	strcat(dirpath, change);
	loadfile_scanf(dirpath, next, png, pentablev2[2]);
	strcpy(dirpath, DLC);
	strcat(dirpath, next);
	strcat(dirpath, trick);
	loadfile_scanf(dirpath, next, png, pentablev2[3]);
}
// 加载自定义图片
void loadfile_scanf(char* dirname, char* next, char* kind, IMAGE** pentablev2)
{
	// 文件存储信息结构体
	struct _finddata_t fileinfo;
	// 保存文件句柄
	long fHandle;
	// 文件数记录器
	char dirpath[100];
	strcpy(dirpath, dirname);
	if (access(dirpath, 0) == -1)													// 检查文件夹是否存在,不存在为 -1
	{
		return;
	}
	else
	{
		int i = -1;																	// 记录文件数量
		char kindpath[100];
		strcpy(kindpath, dirpath);
		strcat(kindpath, next);
		strcat(kindpath, kind);
		if ((fHandle = _findfirst(kindpath, &fileinfo)) == -1L) 					// *是通配符,默认在当前文件夹内查找文件,这里查找 .png 文件
		{
			printf("当前目录: %s 下没有所需文件\n", dirname);
			return;
		}
		else
		{
			char path[100];
			do
			{
				i++;
				printf("在%s 下找到文件:%s,文件大小:%ld bit\n", dirname, fileinfo.name, fileinfo.size);
				strcpy(path, dirname);
				strcat(path, next);
				strcat(path, fileinfo.name);
				while (pentablev2[i] != NULL)
				{
					i++;
				}
				pentablev2[i] = new IMAGE(30, 30);
				loadimage(pentablev2[i], path, 30, 30);								// 根据名称读取文件
			}
			while (_findnext(fHandle, &fileinfo) == 0);
		}
		// 关闭文件
		_findclose(fHandle);

		printf("文件数量:%d\n", i + 1);
	}
}
// 检查边界
inline void checkboundary(int* bkgameleft, int* bkgametop, int gamelimitright, int gamelimitbottom, int* bkmeshgameleft, int* bkmeshgametop, int* meshlimitright, int* meshlimitbuttom, int bkmeshcopywidth, int bkmeshcopyheight)
{
	if (*bkgameleft < 0)												// 网格越界检测并调整
		*bkgameleft = 0;
	else if (*bkgameleft > gamelimitright)								// 超过九宫格的边界就会刷新,所以要 gamelimiright = mapwidth - bkmeshcopywidth,这样就不超过九宫格边界了。
		*bkgameleft = gamelimitright;
	if (*bkgametop < 0)
		*bkgametop = 0;
	else if (*bkgametop > gamelimitbottom)								// 超过九宫格的边界就会刷新,所以要 gamelimitbottom = mapheight - bkmeshcopyheight,这样就不超过九宫格边界了。
		*bkgametop = gamelimitbottom;
	if (*bkgameleft < *bkmeshgameleft)									// 更新游戏采样区坐标,一些简单换算,由于频繁调用函数在这里产生了明显的卡顿影响,所以这里就不再封装成函数
	{
		*bkmeshgameleft -= bkmeshcopywidth;								// 追随玩家,九宫格左移一格宫格,则界限也左移一格宫格的像素宽度
		*meshlimitright -= bkmeshcopywidth;							// 缓存区网格左上角的像素坐标在右界限,移动相同距离,这里改完,发现需要在初始化时规定好边界距离整好是四宫格的宽高
	}
	else if (*bkgameleft > *meshlimitright)
	{
		*meshlimitright += bkmeshcopywidth;
		*bkmeshgameleft += bkmeshcopywidth ;							// 缓存区网格左上角的像素坐标在右界限,移动相同距离,这里改完,发现需要在初始化时规定好边界距离整好是四宫格的宽高
	}
	if (*bkgametop < *bkmeshgametop)
	{
		*bkmeshgametop -= bkmeshcopyheight;
		*meshlimitbuttom -= bkmeshcopyheight;							// 缓存区网格左上角的像素坐标在右界限,移动相同距离,这里改完,发现需要在初始化时规定好边界距离整好是四宫格的宽高
	}
	else if (*bkgametop > *meshlimitbuttom)
	{
		*meshlimitbuttom += bkmeshcopyheight;
		*bkmeshgametop += bkmeshcopyheight;								// 缓存区网格左上角的像素坐标在右界限,移动相同距离,这里改完,发现需要在初始化时规定好边界距离整好是四宫格的宽高
	}
}
// 性能:CPU 2% 占有率,峰值 1.20GHz,核显 GPU 10% 使用率
inline void animation(int characterflag, int bkgameleft, int bkgametop, int bkmeshgameleft, int bkmeshgametop, int bkmeshdeskleft, int bkmeshdesktop)
{
	static int i = 1;
	static int x = 10;
	static int y = 10;
	if (i % 3 == 0)
	{
		x = bkmeshdeskleft + bkgameleft - bkmeshgameleft;
		y = bkmeshdesktop + bkgametop - bkmeshgametop;
		i %= 125;														// i 的上限 * 放大倍数 不超过 255
		switch (characterflag)											// 选取颜色
		{
			case 1:
				setfillcolor(RGB(i * 2, i * 2, 0));
				break;
			case 2:
				setfillcolor(RGB(i * 2, 0, i * 2));
				break;
			case 3:
				setfillcolor(RGB(i * 2, 0, 0));
				break;
			case 4:
				setfillcolor(RGB(0, i * 2, 0));
				break;
			case 5:														// 如果没有按键,则使用默认颜色
				setfillcolor(RGB(0, 0, i * 2));
				break;
		}
		fillrectangle(x, y, x + 30, y + 30);
	}
	i++;
}

inline void animationv2(int characterflag, int bkgameleft, int bkgametop, int bkmeshgameleft, int bkmeshgametop, int bkmeshdeskleft, int bkmeshdesktop)
{
	static int i = 1;
	static int x = 10;
	static int y = 10;
	if (i % 3 == 0)
	{
		x = bkmeshdeskleft + bkgameleft - bkmeshgameleft;
		y = bkmeshdesktop + bkgametop - bkmeshgametop;
		i %= 125;														// i 的上限 * 放大倍数 不超过 255
		switch (characterflag)											// 选取颜色
		{
			case 1:
				setfillcolor(RGB(i * 2, i * 2, 0));
				break;
			case 2:
				setfillcolor(RGB(i * 2, 0, i * 2));
				break;
			case 3:
				setfillcolor(RGB(i * 2, 0, 0));
				break;
			case 4:
				setfillcolor(RGB(0, i * 2, 0));
				break;
			case 5:															// 如果没有按键,则使用默认颜色
				setfillcolor(RGB(0, 0, i * 2));
				break;
		}
		fillrectangle(x, y, x + 30, y + 30);
	}
	i++;
}
// 从默认文件夹中读取自定义图片
void loadfile(IMAGE*** pentablev2,int pixnum)
{
	char brick[100] = "aa_birck_6_family";
	char ground[100] = "ab_ground_7_family";
	char change[100] = "ac_change_8_family";
	char trick[100] = "ad_trick_9_family";
	char next[100] = "./";
	char png[100] = "*.png";
	loadfile_scanf(brick, next, png, pentablev2[0],pixnum);								// 加载用于当砖头类型的图片
	loadfile_scanf(ground, next, png, pentablev2[1],pixnum);
	loadfile_scanf(change, next, png, pentablev2[2],pixnum);
	loadfile_scanf(trick, next, png, pentablev2[3],pixnum);
}
// 加载自定义图片
void loadfile_scanf(char* dirname, char* next, char* kind, IMAGE** pentablev2,int pixnum)
{
	// 文件存储信息结构体
	struct _finddata_t fileinfo;
	// 保存文件句柄
	long fHandle;
	// 文件数记录器
	char dirpath[100];
	strcpy(dirpath, dirname);
	if (access(dirpath, 0) == -1)													// 检查文件夹是否存在,不存在为 -1
	{
		mkdir(dirpath);
		return;
	}
	else
	{
		int i = -1;																	// 记录文件数量
		char kindpath[100];
		strcpy(kindpath, dirpath);
		strcat(kindpath, next);
		strcat(kindpath, kind);
		if ((fHandle = _findfirst(kindpath, &fileinfo)) == -1L) 					// *是通配符,默认在当前文件夹内查找文件,这里查找 .png 文件
		{
			printf("当前目录: %s 下没有所需文件\n", dirname);
			return;
		}
		else
		{
			char path[100];
			do
			{
				i++;
				printf("在%s 下找到文件:%s,文件大小:%ld bit\n", dirname, fileinfo.name, fileinfo.size);
				strcpy(path, dirname);
				strcat(path, next);
				strcat(path, fileinfo.name);
				while (pentablev2[i] != NULL)
				{
					i++;
				}
				// 导入新图片这里需要改动,自适应图片,用于各种大小图片加入,配合剪切
				pentablev2[i] = new IMAGE(pixnum, pixnum);
				loadimage(pentablev2[i], path, pixnum, pixnum);								// 根据名称读取文件
			}
			while (_findnext(fHandle, &fileinfo) == 0);
		}
		// 关闭文件
		_findclose(fHandle);

		printf("文件数量:%d\n", i + 1);
	}
}
// 在目标 DLC 里创建四个文件夹并保存
void savepersonalfile(char* DLC, IMAGE*** pentablev2)
{
	char brick[100] = "aa_birck_6_family";
	char ground[100] = "ab_ground_7_family";
	char change[100] = "ac_change_8_family";
	char trick[100] = "ad_trick_9_family";
	char next[100] = "./";
	char png[100] = ".png";
	if (access(DLC, 0) == -1)														// 检查文件夹是否存在,不存在为 -1
	{
		mkdir(DLC);
	}
	char sign[10] = {};
	strcpy(sign, "6");
	savelocal(DLC, brick, next, png, sign, pentablev2[0]);
	strcpy(sign, "7");
	savelocal(DLC, ground, next, png, sign, pentablev2[1]);
	strcpy(sign, "8");
	savelocal(DLC, change, next, png, sign, pentablev2[2]);
	strcpy(sign, "9");
	savelocal(DLC, trick, next, png, sign, pentablev2[3]);
}
// 保存自定义文件
void savelocal(char* DLC, char* dirname, char* next, char* kind, char* sign, IMAGE** files)
{
	char dirpath[100] = "";
	strcpy(dirpath, DLC);															// 字符串拼接出相对路径
	strcat(dirpath, next);
	strcat(dirpath, dirname);
	if (access(dirpath, 0) == -1)													// 检查 DLC 内的指定文件夹是否存在,不存在为 -1
	{
		mkdir(dirpath);
	}
	char filepath[100];
	char filepa[100];
	char num[10];
	strcpy(filepath, dirpath);														// 字符串拼接出文件名前缀
	strcat(filepath, next);
	strcat(filepath, sign);
	for (int i = 0; i < 10; i++)
	{
		sprintf(num, "%d", i);
		strcpy(filepa, filepath);													// 从头粘贴,可以覆盖之前的信息,不受上一次保存的影响
		strcat(filepa, num);
		strcat(filepa, kind);
		if (files[i] != NULL)
		{
			saveimage(filepa, files[i]);
			printf("file save %s\n", filepa);
		}
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HarmonyOS ArkUI实战开发-网页加载(Web)

移动应用开发中&#xff0c;网页使用的场景非常多&#xff0c;比如在APP内安排一个优惠活动啥的&#xff0c;就可以直接加载一个H5页面高效并且及时&#xff0c;也省去了使用原生开发要升级版本的麻烦&#xff0c;ArkUI开发框架提供了 Web 组件来加载一个网页&#xff0c;本节笔…

音频文件太大了怎么办?如何实现音乐内存压缩?超实用的音频压缩技巧分享给你

一&#xff0c;我们需要了解音乐文件是如何存储的。 音乐文件通常以数字格式存储&#xff0c;如 MP3、WAV、FLAC等。这些格式各有优缺点&#xff0c;但共同点是它们都需要占用一定的存储空间。文件大小取决于多个因素&#xff0c;包括音频质量、编码格式和采样率等。因此&…

网盘_游戏_博客自动化部署(Nginx多项目部署)

目录 一.前提介绍 二.环境介绍 三.自述&#xff08;脚本&#xff09; 四.关于Nginx多项目部署 一.前提介绍 在我之前的博客里详细介绍了上述项目的部署&#xff0c;那么如何使用简单脚本自动部署和使用Nginx多项目部署是本文来介绍的基础篇章。 二.环境介绍 CentOS Linux…

线性表的顺序存储如何设计实现?

如何存储 顺序及链式实现 计算机中的状态

springboot的开发流程

文章目录 springboot的开发流程 1.创建maven项目2.引用依赖 1&#xff09;起步依赖2&#xff09;项目依赖3.启动类4.配置文件5.业务代码 1)dto2)controller6.restful测试7.部署 1&#xff09;打包2&#xff09;部署 springboot的开发流程 1.创建maven项目 新建maven项目 配置…

键盘打字练习游戏代码

效果图 部分代码 index.html <!DOCTYPE html> <html lang"zh"> <head> <meta charset"UTF-8" /> <meta name"viewport" content"widthdevice-width, initial-scale1.0" /> <meta http-equiv"…

编程新手必看,Python3中File(文件) 方法知识点及语法学习总结(24)

**介绍&#xff1a; Python3 中的file对象提供了多种方法来操作文件&#xff0c;以下是一些常用的文件方法&#xff1a; close()&#xff1a;关闭一个已打开的文件。这是释放文件资源的重要步骤&#xff0c;通常在文件操作完成后调用。flush()&#xff1a;刷新文件的缓冲区&am…

vos3000外呼系统客户端无法安装如何解决?

如果 VOS3000 外呼系统客户端无法安装&#xff0c;可以尝试以下解决方法&#xff1a; 检查系统要求&#xff1a; 确保你的计算机满足 VOS3000 外呼系统客户端的系统要求&#xff0c;包括操作系统版本、内存、处理器等。如果系统不符合要求&#xff0c;可能会导致安装失败或者运…

AI生图美学在淘宝的实践应用

本文介绍了如何制定和应用美学标准来评估和改善人工智能生成的图像质量&#xff0c;特别是在电商领域的应用&#xff0c;主要分为制定美学标准、训练美学模型、应用美学模型、升级淘宝风格模型四个步骤。 美学的定义与分析 图像质量标准&#xff1a;现代设计框架下&#xff0c;…

《自动机理论、语言和计算导论》阅读笔记:p261-p314

《自动机理论、语言和计算导论》学习第 10 天&#xff0c;p261-p314总结&#xff0c;总计 48 页。 一、技术总结 1.generating & reachable 2.Chomsky Normal Form(CNF) 乔姆斯基范式。 3.pumping lemma 泵作用引理。引理&#xff1a;引理是数学中为了取得某个更好的…

JAVA基础之垃圾收集器

一 JVM垃圾收集 分代收集思想 当前虚拟机的垃圾收集一般采用分代收集算法&#xff0c;这种算法本身没有创新性&#xff0c;只是根据对象存活周期的不同将内存分为几块。一般将java堆内存分为新生代和老年代&#xff0c;这样我们就可以根据不同年龄到的特点选择不同的垃圾收集…

试用花生壳软件,实现外网访问内网web服务器

试用花生壳软件&#xff0c;实现外网访问内网web服务器。今天查看了一下家用的WiFi路由器和光猫。在wifi路由器里看到了DDNS&#xff0c;看到了花生壳。这时想到了花生壳软件能实现外网访问内网web服务器的功能。于是试用了一下。 先游览了贝锐花生壳公司网站&#xff0c;了解…

落地灯几瓦合适?落地护眼灯推荐

不少人选择在客厅中摆放落地灯的时候&#xff0c;都不知道客厅落地灯多少瓦合适&#xff0c;接下来小编为大家整理了有关客厅落地灯多少瓦合适的相关内容&#xff0c;顺便分享一下落地灯应该怎么选&#xff0c;以及推荐五款落地护眼灯&#xff0c;供大家在选择的时候参考。 灯光…

如何使用ChatGPT仿写一篇学术论文

点击下方▼▼▼▼链接直达AIPaperPass &#xff01; AIPaperPass - AI论文写作指导平台 目录 1.仿写的目的 2.根据专业方向搜集合适的文献 3.总结想要仿写的文献 4.使用ChatGPT一步一步仿写 5.书籍介绍 AIPaperPass智能论文写作平台 深入地阅读和分析你研究领域的相关文…

路由过滤实验

目录 一、实验拓扑 ​编辑 二、实验要求 三、实验思路 四、实验配置 1、配置IP地址 2、配置RIP 3、配置OSPF 4、双向引入 5、使用路由过滤&#xff08;acl&#xff09;&#xff0c;除 R4 上的业务网段以外&#xff0c;其他业务网段路由都引入到对方协议内部 6、配置地…

【函数式接口使用✈️✈️】配合策略模式实现文件处理的案例

目录 &#x1f378;前言 &#x1f37b;一、功能描述 &#x1f37a;二、面向对象设计模式 &#x1f379;三、策略模式 &#x1f366;四、策略 VS 面向对象 &#x1f368;章末 &#x1f378;前言 小伙伴们大家好&#xff0c;上周初步了解了下函数式接口&#xff0c;Consume…

通过DaoCloud DCE的场景化应用看容器云平台的核心能力(三)

以下场景来自DaoCloud官方文档场景化视频&#xff0c;这里以文字形式简单提取下要点&#xff0c;包括操作步骤和一些问题。 一共13个场景&#xff0c;本篇包含5个&#xff1a;9.快速定位异常与排障、10.基于CICD的应用发布、11.基于GitOps持续部署云原生应用、12.使用中间件与…

数据结构与算法笔记:线性建堆

ACM大牛带你玩转算法与数据结构-课程资料 本笔记属于船说系列课程之一&#xff0c;课程链接&#xff1a; 哔哩哔哩_bilibilihttps://www.bilibili.com/cheese/play/ep66799?csourceprivate_space_class_null&spm_id_from333.999.0.0 你也可以选择购买『船说系列课程-年度会…

110. turtle库创建画笔对象

110. turtle库创建画笔对象 【目录】 文章目录 110. turtle库创建画笔对象1. 知识回顾-类与对象1.1 类1.2 对象 2. 创建画笔对象2.1 方法12.1 方法2 3. 绘制一个正方形4. 总结 【正文】 1. 知识回顾-类与对象 类是创建对象的蓝图。 对象是类的实例。 1.1 类 类&#xff08;…

001-谷粒商城-微服务剖析

1、架构图 还是很强的&#xff0c;该有的都有 2、微服务模块 SpringCloudAlibaba组件包括 SentinelNacosRocketMQSeata 搭配SpringCloudAlibaba组件 OpenFeignGateWayRibbn gateway使用了SpringWebFlux&#xff0c;前几天研究到&#xff0c;为什么springboot不直接使用Spri…