- C20250099's blog
坦克大战游戏(转
- 2022-10-2 18:59:59 @
创建一个“坦克大战”文件夹
在里面创建一个CPP代码,命名为“坦克大战.cpp”
代码如下:
#include<iostream>
#include<cstring>
#include<string>
#include<vector>
#include<windows.h>
#include<conio.h>
#include<ctype.h>
#include<ctime>
#include<cstdio>
#include<list>
#define ZERO SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), {0,0})
#define POSAT(a,b) SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), {(a),(b)})
using namespace std;
struct Point{
int x,y;
};
class Prt{
private:
int length = 0;
int Len = 60; //边框宽
int padding = 2; //上下内边距
int margin = 2; //外边距
int win = 1; //如果是win10,就改为1,如果是win7,就改为2
public:
void setPadding(int padding_){padding = padding_;}
void setMargin(int margin_){margin = margin_;}
int getPadding(){return padding;}
int getMargin(){return margin;}
void mar(); //输出左右外边距
void pLine(char str[],int len_); //输出标题
void print(char str[]); //输出一行
void borP(char str[]); //按自动套边框的格式输出,如果框内要输出多行,就用&符连接,如果要输出分割线,就输入~符替代
void borPLogo(char str[]); //带logo的套边框格式输出
};
struct Map{ //地图
int x; //地图宽,不包括边墙
int y; //地图长
char name[30];
char map_[200][200]; //地图,下标从1开始
int lifeTotal_e; //敌方坦克数量
};
class Tank{ //坦克类
private:
int x,y; //坐标
int camp; //所属阵营 ,1为友军,2为敌军
int dir; //脸朝方向,1 2 3 4 对应 左上右下
bool life = true; //是否存活
char shape[3];
clock_t moveLastTime = 0;
clock_t fireLastTime = 0;
public:
static int fire_cd_e;
static int fire_cd_f; //开炮冷却时间 ,单位:毫秒
static int move_cd_e;
static int move_cd_f;
static int reborn_cd;
static int e; //场上敌方坦克总数
static int max_e; //最多上场敌方坦克数量
Tank();
Tank(int x_,int y_,int camp_,int dir_);
Tank(const Tank& t);
~Tank();
void move(); //移动
void fire(); //开炮
bool isLife(){return life;}
void kill(); //杀死该坦克 ,当该坦克死亡时
void turnDir(int dir);
void turnDir(); //无参一般为敌方坦克自动调用
static void createTank_e(); //生成敌方坦克
static void createTank_f(); //生成玩家坦克
void print();
Point getPoint();
};
class Shell{ //炮弹类
private:
int x,y;
int camp;
int dir; //移动方向
int life = true;
char shape[3];
clock_t _time = 0;
public:
static int move_cd; //移动冷却时间 ,单位:毫秒
Shell();
Shell(int x_,int y_,int camp_,int dir_);
void move(); //移动
bool isLife(){return life;}
void kill(){life = false;}
void print();
Point getPoint();
};
class Prop{ //道具类
private:
int id; //指明是哪个道具
int x,y; //坐标
char shape[3]; //在地图上的显示
clock_t propTime = 0; //道具效果当前发动时间
clock_t max_propTime; //道具效果持续时间
clock_t maxTime = 8000; //道具存活时间
clock_t lifeTime; //道具当前存活时间
bool pickup = false;
int tmp; //存放道具改变的属性的原来值,时间一过再变回来
public:
friend void delProp();
Prop(int id_,int x_,int y_);
bool ispickup(){return pickup;}
void pup(){pickup = true;}
void show(); //道具发动效果,拾取时调用
bool isLife(){return clock()-lifeTime<maxTime;}
void print(); //打印到地图上
Point getPoint();
bool regain(); //让时限已到的道具效果失效 ,返回是否可以删了
};
struct Game{ //游戏对象
vector<Map> maps; //标准地图库
Map map; //该局游戏地图
Map tmpMap; //每一次刷新时输出的地图
list<Tank> tank; //坦克
list<Shell> shell; //炮弹
Tank t_player1; //玩家1的坦克
Tank t_player2; //玩家2的坦克
int refreshMapTime = 50; //刷新时间间隔
int lifeTotal_f; //玩家剩余坦克数量
int lifeTotal_e; //敌军该局坦克剩余数量
Prop *prop;
bool gameOver;
bool isDouble;
bool gaming = false;
bool win = false; //关卡胜利
Point core; //核心位置
int num; //关卡
double OSClock; //程序运行时间
double gameClock; //该局游戏时间
char printMap[200][200][3]; //存放输出游戏面板模样
void refreshGame(); //刷新游戏数据,切换关卡时适用
void printGame(); //输出游戏面板 ,刷新游戏调用
int ran; //一个比较客观公正的随机数,因为没有受到条件制约
};
//←↑→↓╢╧╟╤
//█▓■∞
//全局变量区
int row,col;
Prt p;
int mTime = 1200; //提示滞留时间
Game g;
//初始化静态成员
int Tank::e = 0;
int Tank::max_e = 5;
int Tank::fire_cd_e = 800;
int Tank::fire_cd_f = 800; //开炮冷却时间 ,单位:毫秒
int Tank::move_cd_e = 1000; //敌方坦克移动冷却时间
int Tank::move_cd_f = 100; //玩家坦克移动冷却时间
int Tank::reborn_cd = 1500; //坦克重生冷却时间
int Shell::move_cd = 100; //炮弹移动冷却时间
//函数申明区
inline void init(); //初始化
void startGame(); //开始游戏模块
void control(); //设置
void help(); //帮助
void ex(); //退出
//功能模块:敌方坦克开火前自动调用,查找在这个方向上有没有玩家坦克
bool havePlayer(Point p,int checkDir);
int getNum(int min,int max,int thisNum); //让用户输入一个值,只有输入的值在min~max之间时,才结束返回这个值
void setBorder();
void setColor();
void delProp(); //删除道具
void dosClear(int x,int y,int n,int choose); //清屏,x y对应起始位置,n代表有几个选项
//函数实现区
int main(){
init();
int choose = 1;
char key;
short x = 45+p.getMargin()*2,y = 24+p.getMargin()+p.getPadding(); //对应窗口里的列和行
bool back = false; //从其他函数回来
p.setMargin(11);
p.borP("&欢迎试玩抽象坦克大战0.1beta测试版&~&&&上下选择: W S或↑↓&确认选择:Enter &坦克移动: \
&P1:W S A D &P2: ←↑→↓&坦克开火: &P1: J P2: P&&&~&可随时在帮助选项中查看操作方式\
&请按Enter键继续");
p.setMargin(2);
while(1){ //按回车方可继续
key = getch();
if(key==13)break;
}
system("cls");
p.borPLogo("&~&& 开始游戏 & 设置 & 帮助 & 退出 ");
while(1){
dosClear(x,y,4,choose);
switch(choose){
case 1:POSAT(x,y);break;
case 2:POSAT(x,y+1);break;
case 3:POSAT(x,y+2);break;
case 4:POSAT(x,y+3);break;
}
cout << "->";
if(kbhit()){ //如果有键盘键入
key = getch();
switch(key){
case 'w':choose = choose==1?4:choose-1;break;
case 's':choose = choose==4?1:choose+1;break;
case 13: //Enter
switch(choose){
case 1:startGame();break;
case 2:control();break;
case 3:help();break;
case 4:ex();break;
}
system("cls");
x = 45+p.getMargin()*2,y = 24+p.getMargin()+p.getPadding();
p.borPLogo("&~&& 开始游戏 & 设置 & 帮助 & 退出 ");
init();
break;
}
if(key<0||key>128){
key = getch();
switch(key){
case 72:choose = choose==1?4:choose-1;break;
case 80:choose = choose==4?1:choose+1;break;
}
}
}
ZERO; //光标置为0,0
}
return 0;
}
inline void init(){
//初始化随机数种子
srand(unsigned(time(0)));
//隐藏光标
CONSOLE_CURSOR_INFO cursor_info = {1, 0};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
SetConsoleTitle("终极抽象坦克大战"); //控制台标题
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
SMALL_RECT rc = {0,0, 110, 40}; // 重置窗口位置和大小
SetConsoleWindowInfo(hOut,true ,&rc);
//读取地图
Map *map;
FILE *fp;
char tmp;
if((fp=fopen("data/maps.txt","r"))!=NULL){
while(!feof(fp)){ //只要文件还有内容没读完,就继续读取
map = new Map;
fscanf(fp,"%s%d %d %d\n",map->name,&map->y,&map->x,&map->lifeTotal_e);
for(int i = 1;i <= map->y;i++){
for(int j = 1;j <= map->x;j++){
if(feof(fp))break;
fscanf(fp,"%c",&map->map_[i][j]);
}
if(feof(fp))break;
fscanf(fp,"%c",&tmp);
}
g.maps.push_back(*map); //导入游戏对象的标准地图库
}
}
else{
cerr << "error!maps.txt打开失败!" << endl;
}
fclose(fp);
//初始化游戏对象
g.gaming = false;
g.tank.clear();
g.shell.clear();
g.lifeTotal_f = 3;
g.num = 0;
g.lifeTotal_e = g.map.lifeTotal_e;
g.gameOver = false;
g.OSClock = 0;
}
//游戏流程
void startGame(){
system("cls");
int key;
short x = 46+p.getMargin()*2,y = 25+p.getMargin()+p.getPadding();
//选择单双人
if(!g.gaming){ //如果没有正在进行的游戏
int choose = 1;
bool flag = true;
p.borPLogo("&~&&& 单人游戏& 双人游戏");
while(flag){
dosClear(x,y,2,choose);
switch(choose){
case 1:POSAT(x,y);break;
case 2:POSAT(x,y+1);break;
}
cout << "->";
key = getch();
switch(key){
case 'w':choose = choose==1?2:1;break;
case 's':choose = choose==2?1:2;break;
case 13:flag = false;break;
case 27:return;
}
if(key<0||key>128){
key = getch();
switch(key){
case 72:choose = choose==1?2:1;break;
case 80:choose = choose==2?1:2;break;
}
}
ZERO;
}
system("cls");
g.isDouble = choose==2?true:false;
g.refreshGame();
}
//三份内存存放地图,一份存放该关卡地图源文件,一份存放这局游戏中地图的模样,一份存放要打印的地图模样
while(!g.gameOver){
g.ran = rand();
//生成敌方坦克
Tank::createTank_e();
//刷新界面
g.printGame();
//敌方坦克行走
for(Tank& tmp:g.tank){
tmp.move();
}
//敌方坦克开火
for(Tank& tmp:g.tank)
tmp.fire();
//炮弹移动
for(list<Shell>::iterator it = g.shell.begin();it!=g.shell.end();){
it->move();
if(it->isLife()==false){
g.shell.erase(it++);
continue;
}
it++;
}
if(g.prop!=NULL&&g.prop->regain())delProp(); //被拾起且不是时效道具或时效时间已过
if(g.prop!=NULL&&g.prop->isLife()==false&&g.prop->ispickup()==false)delProp(); //寿命已尽并且没有被拾起
if(kbhit()){ //如果有键盘被按下
key = getch();
switch(key){
case 'a':g.t_player1.turnDir(1);g.t_player1.move();break;
case 'w':g.t_player1.turnDir(2);g.t_player1.move();break;
case 'd':g.t_player1.turnDir(3);g.t_player1.move();break;
case 's':g.t_player1.turnDir(4);g.t_player1.move();break;
case 'j':g.t_player1.fire();break;
case 27:return;
}
if(key<0||key>128){
key = getch();
switch(key){
case 75:g.t_player2.turnDir(1);g.t_player2.move();break;
case 72:g.t_player2.turnDir(2);g.t_player2.move();break;
case 77:g.t_player2.turnDir(3);g.t_player2.move();break;
case 80:g.t_player2.turnDir(4);g.t_player2.move();break;
}
}
else if(g.isDouble&&key=='p')g.t_player2.fire();
}
//如果场上敌方坦克都没了并且没有储备命,则进入下一关
if(g.win){
g.refreshGame();
g.lifeTotal_f+=3; //加三条命
}
}
Sleep(400);
system("cls");
p.borP("&&GAME OVER&&");
Sleep(mTime);
init();
}
void control(){
system("cls");
int choose = 1;
int key;
short x = 44+p.getMargin()*2,y = 24+p.getMargin()+p.getPadding();
p.borPLogo(" 设置&~&& 边框属性 & 颜色属性 & 坦克移动速度& 坦克开火速度& 炮弹弹道速度& 上场坦克数量& 玩家坦克数量");
while(1){
dosClear(x,y,7,choose);
switch(choose){
case 1:POSAT(x,y);break;
case 2:POSAT(x,y+1);break;
case 3:POSAT(x,y+2);break;
case 4:POSAT(x,y+3);break;
case 5:POSAT(x,y+4);break;
case 6:POSAT(x,y+5);break;
case 7:POSAT(x,y+6);break;
}
cout << "->";
if(kbhit()){ //如果有键盘键入
key = getch();
switch(key){
case 'w':choose = choose==1?7:choose-1;break;
case 's':choose = choose==7?1:choose+1;break;
case 27:return;
case 13: //Enter
system("cls");
switch(choose){
case 1:setBorder();break;
case 2:setColor();break;
case 3:p.pLine("修改坦克移动速度",60);Tank::move_cd_e = getNum(100,2000,Tank::move_cd_e);break;
case 4:p.pLine("修改坦克开火速度",60);Tank::fire_cd_f = getNum(100,1800,Tank::move_cd_e);break;
case 5:p.pLine("修改炮弹弹道速度",60);Shell::move_cd = getNum(20,1200,Shell::move_cd);break;
case 6:p.pLine("修改上场坦克速度",60);Tank::max_e = getNum(1,10,Tank::max_e);break;
case 7:p.pLine("修改玩家坦克数量",60);g.lifeTotal_f = getNum(1,10,g.lifeTotal_f);break;
}
system("cls");
x = 44+p.getMargin()*2,y = 24+p.getMargin()+p.getPadding();
p.borPLogo(" 设置&~&& 边框属性 & 颜色属性 & 坦克移动速度& 坦克开火速度& 炮弹弹道速度& 上场坦克数量& 玩家坦克数量");
break;
}
if(key<0||key>128){
key = getch();
switch(key){
case 72:choose = choose==1?7:choose-1;break;
case 80:choose = choose==7?1:choose+1;break;
}
}
}
ZERO;
}
}
void setBorder(){
system("cls");
int choose = 1;
int key;
int num;
while(1){
switch(choose){
case 1:p.borP(" 设置&~&&->外边距& 内边距");break;
case 2:p.borP(" 设置&~&& 外边距&->内边距");break;
}
if(kbhit()){ //如果有键盘键入
key = getch();
switch(key){
case 'w':choose = choose==1?2:choose-1;break;
case 's':choose = choose==2?1:choose+1;break;
case 27:return;
case 13: //Enter
switch(choose){
case 1:p.pLine("修改外边距",60); num = getNum(0,4,p.getMargin());p.setMargin(num);break;
case 2:p.pLine("修改内边距",60); num = getNum(0,4,p.getPadding());p.setPadding(num);break;
}
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
SMALL_RECT rc = {0,0, 100+2+p.getMargin()*2*2, 34+p.getMargin()*2+p.getPadding()*2}; // 重置窗口位置和大小
SetConsoleWindowInfo(hOut,true ,&rc);
system("cls");
p.borP(" 修改成功!");
Sleep(mTime);
system("cls");
break;
}
if(key<0||key>128){
key = getch();
switch(key){
case 72:choose = choose==1?2:choose-1;break;
case 80:choose = choose==2?1:choose+1;break;
}
}
}
ZERO;
}
}
void setColor(){
system("cls");
int choose = 1;
int key;
short x = 26+p.getMargin()*2,y = 3+p.getMargin()+p.getPadding();
p.borP("选择颜色&~& 黑底白字& 黄底黑字& 灰底红字& 灰底蓝字& 灰底绿字& 白底黑字");
while(1){
dosClear(x,y,5,choose);
switch(choose){
case 1:POSAT(x,y);break;
case 2:POSAT(x,y+1);break;
case 3:POSAT(x,y+2);break;
case 4:POSAT(x,y+3);break;
case 5:POSAT(x,y+4);break;
case 6:POSAT(x,y+5);break;
}
cout << "->";
if(kbhit()){ //如果有键盘键入
key = getch();
switch(key){
case 'w':choose = choose==1?6:choose-1;break;
case 's':choose = choose==6?1:choose+1;break;
case 27:return;
case 13: //Enter
switch(choose){
case 1:system("color 07");break;
case 2:system("color e0");break;
case 3:system("color 7c");break;
case 4:system("color 79");break;
case 5:system("color a0");break;
case 6:system("color f0");break;
}
}
if(key<0||key>128){
key = getch();
switch(key){
case 72:choose = choose==1?5:choose-1;break;
case 80:choose = choose==5?1:choose+1;break;
}
}
}
}
}
int getNum(int min,int max,int thisNum){
int num;
printf("请输入%d-%d之间的整数(当前为:%d):\n",min,max,thisNum);
scanf("%d",&num);
while(num<min||num>max){
printf("输入不合法,请重新输入:\n");
scanf("%d",&num);
}
return num;
}
void help(){
system("cls");p.borP("帮助中...");Sleep(2000);
}
void ex(){
system("cls");
p.borP("正在退出,请稍后...");
Sleep(1200);
system("cls");
exit(1);
}
void Prt::mar(){
int i = 0;
for(;i < margin;i++)printf(" ");
}
Tank::Tank(){
if(camp==2)e++;
}
Tank::Tank(int x_,int y_,int camp_,int dir_) : x{x_},y{y_},camp{camp_},dir{dir_}{
turnDir(dir_);
if(camp==2)e++;
}
Tank::Tank(const Tank& t){
x = t.x;y = t.y;camp = t.camp;dir = t.dir;
turnDir(dir);
if(camp==2)e++;
}
Tank::~Tank(){
if(camp==2)e--;
}
void Tank::move(){
if(clock()-moveLastTime>(camp==1?move_cd_f:move_cd_e)){
if(camp==2)turnDir(); //如果是敌方坦克,则先需要自动转向
Point change;
bool isNull = true; //脸朝方向前面没有障碍物
switch(dir){
case 1:change.x = x-1;change.y = y;break;
case 2:change.x = x;change.y = y-1;break;
case 3:change.x = x+1;change.y = y;break;
case 4:change.x = x;change.y = y+1;break;
}
if(g.map.map_[change.y][change.x]=='0'){ //地图上这个点是空地
for(Tank tmp:g.tank)
if(tmp.x==change.x&&tmp.y==change.y)isNull = false;
if(g.t_player1.x==change.x&&g.t_player1.y==change.y)isNull = false;
if(g.isDouble&&g.t_player2.x==change.x&&g.t_player2.y==change.y)isNull = false;
//如果这一格确实为空且没坦克,就移动到这个点
if(isNull){
int random = rand()%100;
if(random<50&&g.prop==NULL&&camp==2){ //敌方坦克每次移动,20%的概率在移动前的位置生成一个道具
//只在坦克的周围(除了脸前一格)生成道具
//int px = x+random%3-1,py = y+random%3-1;
//if(px>0&&px<=g.map.x&&py>0&&py<=g.map.y&&px!=change.x&&py!=change.y&&g.map.map_[py][px]=='0')
g.prop = new Prop({g.ran%3+1,x,y});
}
strcpy(g.printMap[y][x]," ");
x = change.x;
y = change.y;
//判断玩家是否拾取道具
if(g.prop&&camp==1&&x==g.prop->getPoint().x&&y==g.prop->getPoint().y){
g.prop->show();
g.prop->pup();
strcpy(g.printMap[g.prop->getPoint().y][g.prop->getPoint().x]," ");
}
}
}
moveLastTime = clock();
}
}
void Tank::fire(){
if(clock()-fireLastTime>(camp==1?fire_cd_f:fire_cd_e)){
if(camp==1){ //如果是玩家坦克,则直接执行开火
g.shell.push_back({x,y,camp,dir});
}
else{
int random = (rand()+1)%100; //1~100随机值
if(random<50){ //只要尝试过开火,不管有没有成功,都再次进入开炮cd
g.shell.push_back({x,y,camp,dir});
}
else{
for(int tmpDir = 1;tmpDir <= 4;tmpDir++){
//如果这个敌方坦克在tmpDir方向从自身坐标到铁块的直线中有玩家坦克,就改变方向,然后开火
if(havePlayer(getPoint(),tmpDir)){
turnDir(tmpDir);
g.shell.push_back({x,y,camp,dir});
}
}
}
}
fireLastTime = clock();
}
}
void Tank::turnDir(int dir_){ //根据脸朝方向修改坦克样式
//╢╧╟╤┤┴├┬
dir = dir_;
switch(dir){
case 1:strcpy(shape,camp==1?"←":"←");break;
case 2:strcpy(shape,camp==1?"↑":"↑");break;
case 3:strcpy(shape,camp==1?"→":"→");break;
case 4:strcpy(shape,camp==1?"↓":"↓");break;
}
}
void Tank::turnDir(){
bool forward = true; //是否可以前进
Point p = this->getPoint(); //脸朝方向的第一格坐标,用于判断是否可以前进
int changeDir = 1;
int backDir = (this->dir+2)%4; //后背方向 ,反方向 ,只有万不得已才返回走
switch(this->dir){
case 1:p.x-=1;break;
case 2:p.y-=1;break;
case 3:p.x+=1;break;
case 4:p.y+=1;break;
}
if(g.map.map_[p.y][p.x]=='2') //如果坦克脸前是铁块,则不能前进
forward = false;
for(Tank& tmp:g.tank) //如果坦克面前有坦克,也不能前进
if(tmp.getPoint().y==p.y&&tmp.getPoint().x==p.x){forward = false;break;}
//开始判断下一步走哪边
int random = (rand()+1)%100;
if(forward&&random<=55){ //如果可以前进,那么60%的概率继续直走
changeDir = dir;
}
else{
vector<int> okDir; //存放能走的方向
if(g.map.map_[y][x-1]!='2'&&backDir!=1&&dir!=1)okDir.push_back(1);
if(g.map.map_[y-1][x]!='2'&&backDir!=2&&dir!=2)okDir.push_back(2);
if(g.map.map_[y][x+1]!='2'&&backDir!=3&&dir!=3)okDir.push_back(3);
if(g.map.map_[y+1][x]!='2'&&backDir!=4&&dir!=4)okDir.push_back(4);
if(okDir.size()>0){
int base = 100.0f/okDir.size(); //获取每个方向被抽中的概率
int idx = 1;
for(int dir:okDir){
if(random>(idx-1)*base&&random<=idx*base) //如果抽中,那么就改为这个方向
changeDir = dir;
idx++;
}
}
else
changeDir = backDir;
}
turnDir(changeDir);
}
void Tank::createTank_e(){
if(Tank::e==0&&g.lifeTotal_e==0){g.win = true;return;}
static clock_t rebornLastTime = 0;
static const int reborn_cd = 1500;
if(clock()-rebornLastTime>reborn_cd){
int random;
bool create_left,create_right,create_f;
//如果场上的敌方坦克数量少于5辆,就生成一辆 ,如果两个生成点都不能生成,这个时间刻度就先不生成
if(Tank::e < Tank::max_e){
random = rand()%2; //随机在左边或右边生成坦克
create_left = create_right = true;
for(Tank tmp:g.tank)
if(tmp.x == 1&&tmp.y == 1)create_left = false;
else if(tmp.x == g.map.x&&tmp.y == 1)create_right = false;
if(g.lifeTotal_e>0)
if(random == 0 && create_left){
g.tank.push_back({1,1,2,4});
g.lifeTotal_e--;
}
else if(random == 1 && create_right){
g.tank.push_back({g.map.x,1,2,4});
g.lifeTotal_e--;
}
}
rebornLastTime = clock();
}
}
void Tank::createTank_f(){
//生成玩家坦克
if(g.t_player1.isLife()==false&&g.lifeTotal_f>0){
g.t_player1 = {g.core.x-2,g.core.y,1,2};
g.lifeTotal_f--;
}
if(g.isDouble&&g.t_player2.isLife()==false&&g.lifeTotal_f>0){
g.t_player2 = {g.core.x+2,g.core.y,1,2};
g.lifeTotal_f--;
}
if(g.lifeTotal_f==0){ //玩家阵营没有储备命了
if(g.t_player1.isLife()==false)
if(!g.isDouble)g.gameOver = true; //如果玩家1死亡并且不是双人,则游戏结束
else if(g.isDouble&&g.t_player2.isLife()==false)g.gameOver = true; //如果是双人且玩家2的坦克也已阵亡,则游戏结束
}
}
void Tank::print(){
strcpy(g.printMap[y][x],shape);
}
void Tank::kill(){
strcpy(g.printMap[y][x]," ");
life = false;
x = 0;y = 0;
}
Point Tank::getPoint(){
Point ret;
ret.x = x;ret.y = y;
return ret;
};
void Shell::move(){ //炮弹的移动
if(clock()-_time>move_cd){
strcpy(g.printMap[y][x]," ");
Point changeP = this->getPoint();
switch(dir){ //炮弹先前进一格,至于死亡后面再判断
case 1:changeP.x-=1;break;
case 2:changeP.y-=1;break;
case 3:changeP.x+=1;break;
case 4:changeP.y+=1;break;
}
switch(g.map.map_[changeP.y][changeP.x]){
case '1':g.map.map_[changeP.y][changeP.x] = '0';this->kill();break; //打到的是砖块,那么砖块消失,炮弹死亡
case '2':this->kill();break; //打到的是铁块,炮弹死亡
case '9':this->kill();g.gameOver = true; //如果打到的是核心,那么游戏结束
}
//检测有没有碰到敌方坦克
for(list<Tank>::iterator tmp = g.tank.begin();tmp!=g.tank.end();){
if(tmp->getPoint().x==changeP.x&&tmp->getPoint().y==changeP.y){
this->kill();
if(camp==1) { //如果是玩家的炮弹,则坦克阵亡
tmp->kill();
g.tank.erase(tmp++);
continue;
}
}
tmp++;
}
//检测有没有碰到其他炮弹
for(list<Shell>::iterator tmp = g.shell.begin();tmp!=g.shell.end();){
if(tmp->getPoint().y==changeP.y&&tmp->getPoint().x==changeP.x){
this->kill();
strcpy(g.printMap[tmp->getPoint().y][tmp->getPoint().x]," ");
g.shell.erase(tmp++);
continue;
}
tmp++;
}
if(g.t_player1.getPoint().x==changeP.x&&g.t_player1.getPoint().y==changeP.y){
this->kill();
if(camp==2)g.t_player1.kill(); //如果是敌方炮弹,则玩家1坦克阵亡
}
if(g.isDouble&&g.t_player2.getPoint().x==changeP.x&&g.t_player2.getPoint().y==changeP.y){
this->kill();
if(camp==2)g.t_player2.kill();
}
if(this->isLife()){x = changeP.x;y = changeP.y;}
_time = clock();
}
Tank::createTank_f();
}
void Shell::print(){
strcpy(g.printMap[y][x],shape);
}
Shell::Shell(){
strcpy(shape,"+ ");
}
Shell::Shell(int x_,int y_,int camp_,int dir_) : x{x_},y{y_},camp{camp_},dir{dir_}{
strcpy(shape,"+ ");
}
Point Shell::getPoint(){
Point p;
p.x = x;p.y = y;
return p;
}
void Game::refreshGame(){ //第一次游戏或切换关卡时调用此函数
g.map = g.maps[++g.num-1]; //根据关卡切换游戏当前地图
g.tmpMap = g.map;
g.gaming= true;
g.win = false;
if(g.prop!=NULL)delProp();
system("cls");
p.borP(g.map.name); //打印关卡数
Sleep(mTime);
system("cls");
//获取核心位置,且给周围一圈加上围墙
for(int i = 0;i <= g.map.y+1;i++)
for(int j = 0;j <= g.map.x+1;j++)
if(i==0||i==g.map.y+1||j==0||j==g.map.x+1) //加围墙
g.map.map_[i][j] = '2';
else if(g.map.map_[i][j]=='9'){
g.core.x = j;
g.core.y = i;
}
//初始化敌方坦克总数
g.lifeTotal_e = g.map.lifeTotal_e;
//生成玩家坦克
g.t_player1 = {g.core.x-2,g.core.y,1,2};g.lifeTotal_f--;
if(isDouble) {
g.t_player2 = {g.core.x+2,g.core.y,1,2};g.lifeTotal_f--;
}
//重置该局游戏时间
g.gameClock = clock();
//初始化游戏面板模样
for(int i = 0;i <= g.map.y+1;i++)
for(int j = 0;j <= g.map.x+1;j++)
switch(g.map.map_[i][j]){
case '0':strcpy(g.printMap[i][j]," ");break;
case '1':strcpy(g.printMap[i][j],"▓");break;
case '2':strcpy(g.printMap[i][j],"█");break;
case '9':strcpy(g.printMap[i][j],"∞");break;
}
}
void Game::printGame(){
static int lastF = 0;
static int lastE = 0;
static clock_t _time = 0;
if(clock()-_time>g.refreshMapTime){
Point tmpPoint;
ZERO; //重置光标位置为0,0
//先更新地图
for(int i = 1;i <= g.map.y;i++)
for(int j = 1;j <= g.map.x;j++)
if(g.map.map_[i][j]!=g.tmpMap.map_[i][j]) //如果上次刷新时地图的这个点和这次不同,就修改
strcpy(g.printMap[i][j]," "); //以目前的规则,方块更新只有可能是砖块变为空
g.tmpMap = g.map; //用完后,更新临时地图
//然后把炮弹放进地图
for(Shell tmp:g.shell)
tmp.print();
//再把坦克放进地图
for(Tank tmp:g.tank)
tmp.print();
if(g.t_player1.isLife())g.t_player1.print();
if(isDouble&&g.t_player2.isLife())g.t_player2.print();
//把道具放入地图
if(g.prop!=NULL&&g.prop->ispickup()==false){g.prop->print();}
//打印游戏面板
for(int i = 0;i <= g.map.y+1;i++){
for(int j = 0;j <= g.map.x+1;j++)
cout << g.printMap[i][j];
cout << endl;
}
//最后打印游戏数据
p.pLine(g.map.name,g.map.x*2+4);
printf("游戏时间:%.1fs\n",(clock()-g.gameClock)/CLOCKS_PER_SEC);
printf("玩家剩余坦克:");
for(int i = 0;i < g.lifeTotal_f;i++)
cout << "↑";
for(int i = g.lifeTotal_f;i < lastF;i++)
cout << " ";
cout << endl;
printf("敌方剩余坦克:");
for(int i = 0;i < g.lifeTotal_e;i++)
cout << "↑";
for(int i = g.lifeTotal_e;i < lastE;i++)
cout << " ";
cout << endl;
lastF = g.lifeTotal_f;
lastE = g.lifeTotal_e;
_time = clock();
}
}
bool havePlayer(Point p,int checkDir){
int changeX = 0,changeY = 0;
switch(checkDir){
case 1:changeX = -1;break;
case 2:changeY = -1;break;
case 3:changeX = 1;break;
case 4:changeY = 1;break;
}
bool havePlayer = false;
while(g.map.map_[p.y][p.x]!='2'){ //只要没碰见铁块方块,就继续查找
if(g.t_player1.getPoint().y==p.y&&g.t_player1.getPoint().x==p.x){
havePlayer = true;break;
}
else if(g.isDouble&&g.t_player2.getPoint().y==p.y&&g.t_player2.getPoint().x==p.x){
havePlayer = true;break;
}
p.x+=changeX;p.y+=changeY;
}
return havePlayer;
}
//输出标题
void Prt::pLine(char str[],int len_){
int i;
int s_len = 0;
for(i = 0;str[i];i++)s_len++;
for(i = 0;i < (len_-s_len)/2;i++)printf("*");
printf("%s",str);
for(i = 0;i < (len_-s_len)/2;i++)printf("*");
if((len_-s_len)%2)printf("*"); //如果中间内容不是双数长,末尾就补一个*号
printf("\n");
}
//输出一行
void Prt::print(char str[]){
int i,s_len = 0;
for(i = 0;str[i];i++)s_len++;
mar(); //每行先输出外边距
printf("│");
for(i = 0;i < (Len-s_len)/2;i++)printf(" ");
printf("%s",str);
s_len = s_len%2?s_len-1:s_len;
for(i = 0;i < (Len-s_len)/2;i++)printf(" ");
printf("│\n");
}
//按自动套边框的格式输出,如果框内要输出多行,就用&符连接
void Prt::borP(char str[]){
int i,s_len = 0;
int begin = 0; //子串从str的哪个元素下标开始复制
bool haveLine = false;
char substr[300];
substr[0] = '\0'; //初始化
for(i = 0;i < margin;i++)printf("\n");
mar();printf("┌");for(i = 0;i < Len/win;i++)printf("─");printf("┐\n");
for(i = 0;i < padding;i++)print("");
for(i = 0;1;i++){
if(str[i]=='&'){substr[(haveLine?Len*2/win:i-begin)] = '\0';begin = i+1;print(substr);substr[0] = '\0';haveLine = false;}
else if(str[i]=='~'){
for(int j = 0;j < Len;j++){
strcat(substr,"─");
}
haveLine = true;
}
else if(str[i] == '\0'){substr[i-begin] = '\0';print(substr);break;}
else
substr[i-begin] = str[i];
}
for(i = 0;i < padding;i++)print("");
mar();printf("└");for(i = 0;i < Len/win;i++)printf("─");printf("┘\n");
for(i = 0;i < margin;i++)printf("\n");
this->Len = 60;
}
void Prt::borPLogo(char str[]){
this->Len = 100;
char logoStr[2000] = {"\
▓▓▓▓▓▓ ▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓ ▓▓▓▓▓▓&\
▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ &\
▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ &\
▓▓▓▓▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓▓▓▓ &\
▓▓ ▓▓ ▓▓▓▓▓▓▓ ▓▓ ▓▓ ▓▓ ▓▓ &\
▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ &\
▓▓▓▓▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓▓&\
&&\
▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓ ▓▓&\
▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓&\
▓▓ ▓▓ ▓▓ ▓▓ ▓▓&\
▓▓ ▓▓ ▓▓ ▓▓▓▓ &\
▓▓ ▓▓ ▓▓ ▓▓ &\
▓▓ ▓▓ ▓▓ ▓▓ ▓▓ &\
▓▓▓▓ ▓▓▓▓▓▓ ▓▓ ▓▓ &\
&&抽象坦克大战0.1beta测试版&&"};
strcat(logoStr,str);
borP(logoStr);
}
Prop::Prop(int id_,int x_,int y_){
id = id_;x = x_;y = y_;
lifeTime = clock();
tmp = 0;
max_propTime = 0;
switch(id){
case 1:strcpy(shape,"弹");break;
case 2:strcpy(shape,"速");break;
case 3:strcpy(shape,"命"); break;
}
}
void Prop::show(){
propTime = clock();
switch(id){
case 1:
for(list<Tank>::iterator it = g.tank.begin();it!=g.tank.end();){ //清除所有场上敌方坦克
strcpy(g.printMap[it->getPoint().y][it->getPoint().x] ," ");
g.tank.erase(it++);
}
break;
case 2:
tmp = Tank::fire_cd_f; //提高射速,持续五秒
max_propTime = 5000;Tank::fire_cd_f = 100;break;
case 3:
g.lifeTotal_f++;break; //玩家阵营加一条命
}
}
void Prop::print(){
strcpy(g.printMap[y][x],shape);
}
Point Prop::getPoint(){
Point p;
p.x = x;p.y = y;
return p;
};
bool Prop::regain(){ //是否可以删除了
bool ret = false;
if(ispickup()&&max_propTime>0){ //如果已拾取并且是时效道具
if(clock()-propTime>max_propTime){ //道具时效已到
ret = true;
}
else ret = false;
}
else if(ispickup()&&max_propTime==0)ret = true;
return ret;
}
void delProp(){
if(g.prop->tmp!=0)
switch(g.prop->id){
case 2:Tank::fire_cd_f = g.prop->tmp;break;
}
strcpy(g.printMap[g.prop->getPoint().y][g.prop->getPoint().x]," ");
delete g.prop;g.prop = NULL;
}
void dosClear(int x,int y,int n,int choose){
for(int i = 0;i < n;i++){
if(i+1==choose)continue;
POSAT(x,y+i);
cout << " ";
}
}
在文件夹里再新建一个文件夹叫data
放入一个txt文件
名字叫maps
内容:
“
第一关 25 25 7 0000000000000000000000000 0011100010111101111011110 0011100010111101111011110 0001000010100101001010010 0001000010100101001010010 0101010010111101111010010 0101010010111101111010010 0111110010000100001010010 0111110010000100001010010 0100010010111101111011110 0100010010111101111011110 0000000000000000000000000 0000000000000000000000000 2211111111111111111111122 2211111111111111111111122 0000000000022200000000000 0000000000022200000000000 1111111000001000001111111 0000000000011100000000000 0011111110001000111111100 0000000000000000000000000 1111111000111110001111111 0000000000000000000000000 0011111110011100111111100 0000000000019100000000000 第二关 19 19 8 0000000000000100000 1111010111110101011 0001010001000101000 0101011111011101010 0100010100010001010 0111110101011111110 0000010001000000010 1111110101111111010 0100000100010000010 0101112111011111110 0100000001010000000 0101110111010111111 0100010101000000000 0111120101111221110 0100010100000000010 0101010111111111010 0001010000000001010 0112111111111101011 0000000019100000000
第三关 13 13 6 0000000000000 0101010101010 0101010101010 0101012101010 0101010101010 0101000001010 0000010100000 2201000001022 0000010100000 0101011101010 0101000001010 0101011101010 0000019100000
”
地图说明:
格式: 地图名 地图宽 地图高 地图敌方坦克数量 地图矩阵
9代表核心,0代表空地,1代表砖块,2代表铁块 一份合格地图的要求: 核心左右间隔方块必须为空 地图右上角和左上角两个坐标点必须为空
最后运行坦克大战.cpp