用C语言实现CLI界面的魔塔游戏

简介

本着开源的精神,我分享下我做的数据结构大作业,我当时选择的是游戏设计题目,由于魔塔基础的机制不太复杂,所以就借着大作业设计了个简易的魔塔游戏。
这是游戏界面:
在这里插入图片描述

以下是我当时大作业内容:

我把大作业文档精简下,简单说明下,为了让读者能更好的看懂程序内容。

程序内函数介绍:

(1) main函数用于处理开始界面和调用各种函数
(2) switch_case函数用于返回选择的content_str[]的位置,默认1开始。* case_num:contentstr的数组大小;contentstr[]:选项内容
(3) kbhit_remove函数用于清空按键缓冲区,防止之前的残留按键行为的影响
(4) kbhit_wait_getasc函数用于等待按键并读取按下按键的 ASC码
(5) light_gotoxy函数用于光标移动,x为横坐标,y为纵坐标
(6) light_gotoxy函数用于获取光标位置,x为横坐标,y为纵坐标
(7) maps函数用于判断处于第几层并移交给map_sub函数进行详细处理
(8) map_sub函数用于对相应格子进行具体的输出以及相应的处理,使用平面直角坐标系进行定位
(9) mota_move函数用于处理游戏中每步移动后的情景,1代表上,2代表下,3代表左,4代表右
(10) move_sub函数用于判断相应的楼层并移交给floor_sub函数进行详细处理
(11) floor_sub函数用于根据不同的楼层选择不同的楼层地图,并进行具体的处理
(12) battle函数用于魔塔战斗机制的处理
(13) undefined_1函数用于对话事件的处理
(14) blood_vial函数用于处理碰到血瓶时候恢复的血量
(15) gem_stone函数用于处理碰到宝石时候增加相应的能力值
(16) get_keys函数用于处理碰到钥匙后添加相应的钥匙
(17) open_door函数用于处理开门的事件
(18) cant_door函数用于处理无法打开时,进行的撤回操作
(19) up_down函数用于上下楼的切换并移交给manual_sub函数进行详细处理
(20) monster_manual函数用于判断处于的楼层
(21) manual_sub函数用于怪物手册根据楼层进行具体的处理
(22) monster_sub函数用于怪物手册输出怪物信息
(23) lose_hp函数用于处理丢失血量的算法和显示处理
(24) saving_game函数用于存档功能实现
(25) save_floor函数用于判断处于什么层,并移交给save_sub函数进行详细处理
(26) save_sub函数用于对相应层进行存档的具体处理
(27) loading_game函数用于读档功能实现,读档后因为原来的主角位置会更改,必须对其重新定位
(28) load_floor函数用于判断处于什么层,并移交给load_sub函数进行详细处理
(29) load_sub函数用于对相应层进行读档的具体处理
(30) get_poistion函数用于判断处于什么层,并对每层进行位置搜索
(31) poistion_sub函数用于对每层进行搜索,寻找主角的位置

程序内头文件介绍:

本游戏有3个头文件,actors_info.h用于表示主角的信息,结构体内有15个int变量,结构体别名为actors。创建main_actors结构体变量来指定主角相应的信息。结构体定义和变量定义为:

typedef struct
{
    int level;
    int hp;
    int maxhp;
    int sp;
    int maxsp;
    int str;
    int dex;
    int img;
    int agi;
    int gold;
    int exp;
    int yellow;
    int blue;
    int red;
    int green;
}actors;
actors main_actors={
    -1,800,5000,-1,-1,1,1,1,-1,0,-1,8,2,1,-1
};

enemy_info.h存放怪物信息变量,通过创建结构体别名enemy,然后创建一个结构体数组enemy_data[]。结构体定义和数组定义为:

typedef struct
{
    int id;
    int hp;
    int str;
    int dex;
    int gold;
    int exp;
    int pro;
    char name[20];
}enemy;
enemy enemy_data[AMOUNT]={
    {0,0,0,0,0,0,0,"保留"},
  //后面和前面一样也是结构体形式
};

map_info.h存放魔塔里面的地图信息,通过结构体别名,然后一个楼层创建一个结构体矩阵floorXXX[][].里面就两个参数Info是判断对应物体种类,sub是在这个种类之下判断具体种类。结构体定义和矩阵定义如下

typedef struct
{
    int info;
    int sub;
}mota;
mota floor001[MAP_L][MAP_L]={
    {{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0}},
//后面和前面一样,这样就写出了一行
};

函数调用关系图:

在这里插入图片描述

完整程序如下:

map_info.h(存放地图信息)

//魔塔地图最常见的大小为15*15规模
#define MAP_L 15
//基础倍率*1
int multi=1;
//这里采用结构体,info为物品类型,sub为具体编号(其中0代表不用)
typedef struct
{
    int info;
    int sub;
}mota;
/*这里相当于魔塔里面的地图,使用行下标和列下标表示地图位置,里面填的是此格信息
0-地板,1-墙体,2-敌人,3-对话事件,4-血瓶,5-宝石,6-主角,7-钥匙,8-门,9-切换地图,10-其他物品
2敌人编号详情查看enemy.h文件
4血瓶编号:1-红血瓶,2-蓝血瓶,3-黄血瓶,4-绿血瓶
5宝石编号:1-红宝石,2-蓝宝石,3-绿宝石,4-黄宝石
7钥匙编号:1-黄钥匙,2-蓝钥匙,3-红钥匙,4-绿钥匙
8门编号:1-黄门,2-蓝门,3-红门,4-绿门
9切换地图:1-切换前一个地图,2-切换后一个地图
我这里就实现3层的内容,用来代表魔塔游戏核心算法*/
//其实设计魔塔时候,本质就是这样操作的,只不过在RMXP中通过GUI方式通过图层加入到楼层中
/*在RMXP中将生成的地图加入Data文件夹中的MapXXX.rxdata,
对应实现我这里相当于把所有MapXXX.rxdata地图都放在这个头文件中*/
mota floor001[MAP_L][MAP_L]={
    {{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0}},
    {{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{4,1},{1,0},{7,1},{7,1},{1,0},{0,0},{5,1},{1,0}},
    {{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{0,0},{8,1},{7,1},{7,1},{8,2},{2,5},{0,0},{1,0}},
    {{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{2,3},{1,0},{0,0},{2,4},{1,0},{0,0},{4,2},{1,0}},
    {{1,0},{1,0},{1,0},{1,0},{4,1},{0,0},{1,0},{8,1},{1,0},{1,0},{8,1},{1,0},{1,0},{1,0},{1,0}},
    {{1,0},{5,2},{5,1},{1,0},{0,0},{2,2},{8,1},{0,0},{0,0},{4,1},{0,0},{2,3},{0,0},{9,2},{1,0}},
    {{1,0},{0,0},{2,4},{1,0},{1,0},{8,1},{1,0},{1,0},{8,1},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0}},
    {{1,0},{1,0},{8,2},{1,0},{0,0},{4,1},{1,0},{1,0},{4,1},{1,0},{1,0},{1,0},{5,1},{7,1},{1,0}},
    {{1,0},{4,1},{0,0},{1,0},{2,1},{0,0},{1,0},{2,3},{0,0},{1,0},{1,0},{1,0},{0,0},{2,3},{1,0}},
    {{1,0},{0,0},{2,2},{1,0},{8,1},{1,0},{1,0},{8,1},{1,0},{1,0},{1,0},{1,0},{1,0},{8,1},{1,0}},
    {{1,0},{1,0},{8,1},{1,0},{0,0},{0,0},{2,1},{0,0},{2,2},{0,0},{0,0},{0,0},{2,2},{0,0},{1,0}},
    {{1,0},{4,1},{0,0},{2,1},{0,0},{1,0},{1,0},{8,3},{1,0},{1,0},{1,0},{8,1},{1,0},{1,0},{1,0}},
    {{1,0},{1,0},{1,0},{8,1},{1,0},{1,0},{5,1},{0,0},{5,2},{1,0},{0,0},{2,4},{0,0},{1,0},{1,0}},
    {{1,0},{7,1},{7,1},{2,2},{1,0},{1,0},{1,0},{0,0},{1,0},{1,0},{5,2},{0,0},{7,2},{1,0},{1,0}},
    {{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0}}
    
};
mota floor002[MAP_L][MAP_L]={
    {{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0}},
    {{1,0},{0,0},{2,4},{8,1},{0,0},{7,1},{0,0},{1,0},{5,1},{0,0},{1,0},{0,0},{4,1},{0,0},{1,0}},
    {{1,0},{9,2},{0,0},{1,0},{4,1},{0,0},{2,3},{1,0},{0,0},{2,5},{1,0},{7,1},{0,0},{7,1},{1,0}},
    {{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{8,1},{1,0},{1,0},{8,1},{1,0},{0,0},{2,5},{0,0},{1,0}},
    {{1,0},{0,0},{2,4},{8,1},{0,0},{0,0},{2,4},{0,0},{0,0},{0,0},{1,0},{1,0},{8,1},{1,0},{1,0}},
    {{1,0},{7,2},{0,0},{1,0},{2,3},{1,0},{1,0},{1,0},{1,0},{0,0},{7,1},{7,1},{0,0},{9,1},{1,0}},
    {{1,0},{1,0},{1,0},{1,0},{7,1},{1,0},{5,2},{2,3},{5,1},{1,0},{1,0},{1,0},{8,1},{1,0},{1,0}},
    {{1,0},{1,0},{1,0},{1,0},{2,2},{1,0},{1,0},{8,1},{1,0},{0,0},{0,0},{0,0},{2,3},{4,1},{1,0}},
    {{1,0},{1,0},{1,0},{1,0},{0,0},{0,0},{2,3},{4,1},{2,3},{0,0},{1,0},{1,0},{8,1},{1,0},{1,0}},
    {{1,0},{1,0},{1,0},{1,0},{8,2},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{2,4},{0,0},{1,0}},
    {{1,0},{0,0},{0,0},{2,6},{2,6},{2,6},{0,0},{0,0},{1,0},{1,0},{1,0},{1,0},{0,0},{4,1},{1,0}},
    {{1,0},{4,1},{1,0},{0,0},{0,0},{0,0},{1,0},{4,1},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0}},
    {{1,0},{0,0},{1,0},{1,0},{1,0},{1,0},{1,0},{0,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0}},
    {{1,0},{7,1},{0,0},{5,1},{1,0},{5,2},{0,0},{7,1},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0}},
    {{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0}}
};
mota floor003[MAP_L][MAP_L]={
    {{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0}},
    {{1,0},{0,0},{7,1},{1,0},{0,0},{5,1},{1,0},{0,0},{4,2},{1,0},{5,1},{0,0},{5,2},{1,0},{1,0}},
    {{1,0},{9,1},{0,0},{8,1},{2,3},{0,0},{8,1},{2,5},{0,0},{1,0},{0,0},{2,6},{0,0},{1,0},{1,0}},
    {{1,0},{1,0},{1,0},{1,0},{8,1},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{8,1},{1,0},{1,0},{1,0}},
    {{1,0},{2,5},{8,1},{0,0},{2,3},{0,0},{0,0},{2,6},{0,0},{2,6},{0,0},{0,0},{4,1},{1,0},{1,0}},
    {{1,0},{7,1},{1,0},{8,1},{1,0},{8,2},{1,0},{1,0},{1,0},{1,0},{1,0},{8,1},{1,0},{1,0},{1,0}},
    {{1,0},{7,1},{1,0},{2,7},{0,0},{2,3},{1,0},{1,0},{1,0},{1,0},{4,1},{2,4},{7,1},{1,0},{1,0}},
    {{1,0},{7,1},{1,0},{1,0},{8,1},{1,0},{1,0},{1,0},{1,0},{1,0},{2,4},{0,0},{2,4},{1,0},{1,0}},
    {{1,0},{1,0},{1,0},{0,0},{2,4},{0,0},{1,0},{0,0},{7,1},{1,0},{1,0},{0,0},{1,0},{1,0},{1,0}},
    {{1,0},{1,0},{1,0},{2,7},{1,0},{2,3},{8,1},{2,6},{0,0},{1,0},{4,1},{2,7},{7,1},{1,0},{1,0}},
    {{1,0},{1,0},{1,0},{5,1},{2,7},{0,0},{1,0},{0,0},{4,1},{1,0},{2,7},{0,0},{2,7},{1,0},{1,0}},
    {{1,0},{1,0},{1,0},{1,0},{8,2},{1,0},{1,0},{1,0},{8,1},{1,0},{1,0},{0,0},{1,0},{1,0},{1,0}},
    {{1,0},{1,0},{1,0},{9,2},{2,4},{4,1},{8,1},{0,0},{2,4},{1,0},{0,0},{4,1},{0,0},{1,0},{1,0}},
    {{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{4,1},{0,0},{1,0},{7,1},{0,0},{7,2},{1,0},{1,0}},
    {{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0}}
};
//这个是以防前面出错,可以方便将基本结构的备份进行复制
/*
mota floor_backup[MAP_L][MAP_L]={
    {{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}},
    {{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}},
    {{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}},
    {{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}},
    {{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}},
    {{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}},
    {{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}},
    {{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}},
    {{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}},
    {{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}},
    {{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}},
    {{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}},
    {{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}},
    {{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}},
    {{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}}
};
*/

enemy_info.h(存放怪物数据信息)

//有几个怪物就写几个编号+1,使用define定义后续修改很方便
#define AMOUNT 8
/*怪物相应编号:1-绿头怪,2-红头怪,3-蓝蝙蝠,4-蓝法师,5-骷髅人
6-黑头怪,7-大蝙蝠*/
//怪物相应元素有编号,生命,攻击,防御,金钱,经验,属性(例如魔攻,坚固,夹击等)
typedef struct
{
    int id;
    int hp;
    int str;
    int dex;
    int gold;
    int exp;
    int pro;
    char name[20];
}enemy;
//0号为保留位
enemy enemy_data[AMOUNT]={
    {0,0,0,0,0,0,0,"保留"},
    {1,20,2,1,1,1,0,"绿头怪"},
    {2,30,3,0,1,1,0,"红头怪"},
    {3,45,5,1,3,1,0,"蓝蝙蝠"},
    {4,50,4,2,3,1,0,"蓝法师"},
    {5,60,8,2,4,1,0,"骷髅人"},
    {6,60,6,4,4,1,0,"黑头怪"},
    {7,65,10,4,6,1,0,"大蝙蝠"}
};

actors_info.h(存放主角信息)

/*主角相应元素有等级,生命,最大生命,魔法值,最大魔法值,攻击力,防御力,护盾,攻速,
金钱,经验,黄蓝红绿钥匙数量。在本游戏中用不到等级,经验,魔法值,攻速和绿钥匙(在规
模较大并且机制较复杂的蓝海魔塔中这些属性就都会用到,甚至还要自己添加别的属性)*/
//其实钥匙应该写在另一个结构体,为了简化操作合并在一起了,当然可扩展性相应也变差了
//start变量是游戏是否开始的开关
int start=0;
typedef struct
{
    int level;
    int hp;
    int maxhp;
    int sp;
    int maxsp;
    int str;
    int dex;
    int img;
    int agi;
    int gold;
    int exp;
    int yellow;
    int blue;
    int red;
    int green;
}actors;
//-1代表用不到
actors main_actors={
    -1,800,5000,-1,-1,1,1,1,-1,0,-1,8,2,1,-1
};

main.cpp(游戏主程序)

#include <bits/stdc++.h>
#include <conio.h>
#include <windows.h>
#include "enemy_info.h"
#include "map_info.h"
#include "actors_info.h"
//设置怪物数量目前最大到1000,地图大小为15*15,单层怪物数量为20
//设置当前楼层数,当前为3
#define MAP_L 15
#define MAX_M 50
#define ENEMY_MAX 1000
#define CURRENT_FLOOR 3
//键盘上下左右健其实是一个双字符,第一个字符固定为-32,第二个字符代表相应的操作
//这里采用16进制表示
#define UP 0X48        //上键第二个字符是72
#define LEFT 0X4B    //左键第二个字符是75
#define RIGHT 0X4D    //右键第二个字符是77
#define DOWN 0X50    //下键第二个字符是80
//没有钥匙时候返回值
#define NO_YELLOW -6
#define NO_BLUE -7
#define NO_RED -8
#define NO_GREEN -9
using namespace std;
//对相应格子进行具体的输出以及相应的处理,使用平面直角坐标系进行定位
void map_sub(mota floor[][MAP_L],int x,int y)
{
    switch(floor[x][y].info)
    {
        //地板使用空格表示
    case 0:
        cout<<setw(3)<<" ";
        break;
        //墙壁使用*表示
    case 1:
        cout<<setw(3)<<"*";
        break;
        //具体怪物直接使用相应数字编号来表示
    case 2:
        for(int i=1;i<ENEMY_MAX;i++)
        {
            if(i==floor[x][y].sub)
            {
                cout<<setw(3)<<floor[x][y].sub;
                break;
            }
        }
        break;
        //对话事件就暂时用一个图标代替
    case 3:
        cout<<setw(3)<<"♂";
        break;
        //红血瓶使用□表示,蓝血瓶使用☆表示,黄血瓶使用*表示,绿血瓶使用√表示
    case 4:
        switch(floor[x][y].sub)
        {
        case 1:
            cout<<setw(3)<<"□";
            break;
        case 2:
            cout<<setw(3)<<"☆";
            break;
        case 3:
            cout<<setw(3)<<"*";
            break;
        case 4:
            cout<<setw(3)<<"√";
            break;
        }
        break;
        //红宝石使用■表示,蓝宝石使用★表示,绿宝石使用§表示,黄宝石使用◎表示
    case 5:
        switch(floor[x][y].sub)
        {
        case 1:
            cout<<setw(3)<<"■";
            break;
        case 2:
            cout<<setw(3)<<"★";
            break;
        case 3:
            cout<<setw(3)<<"§";
            break;
        case 4:
            cout<<setw(3)<<"◎";
            break;
        }
        break;
        //主角直接使用‘主’字来表示
    case 6:
        cout<<setw(3)<<"主";
        break;
        //黄钥匙使用△表示,蓝钥匙使用▽表示,红钥匙使用○表示,绿钥匙使用◇表示
    case 7:
        switch(floor[x][y].sub)
        {
        case 1:
            cout<<setw(3)<<"△";
            break;
        case 2:
            cout<<setw(3)<<"▽";
            break;
        case 3:
            cout<<setw(3)<<"○";
            break;
        case 4:
            cout<<setw(3)<<"◇";
            break;
        }
        break;
        //黄门使用▲表示,蓝门使用▼表示,红门使用●表示,绿门使用◆表示
    case 8:
        switch(floor[x][y].sub)
        {
        case 1:
            cout<<setw(3)<<"▲";
            break;
        case 2:
            cout<<setw(3)<<"▼";
            break;
        case 3:
            cout<<setw(3)<<"●";
            break;
        case 4:
            cout<<setw(3)<<"◆";
            break;
        }
        break;
        //传送到上个地图用←表示,传送到下个地图用→表示
    case 9:
        switch(floor[x][y].sub)
        {
        case 1:
            cout<<setw(3)<<"←";
            break;
        case 2:
            cout<<setw(3)<<"→";
            break;
        }
        break;
        //由于这个3层内没有其余物品,暂时用&代替
    case 10:
        cout<<setw(3)<<"&";
        break;
    }
}

//判断处于第几层并移交给map_sub进行详细处理
int maps(int floor)
{
    for(int i=0;i<MAP_L;i++)
    {
        for(int j=0;j<MAP_L;j++)
        {
            switch(floor)
            {
            case 1:
                map_sub(floor001,i,j);
                break;
            case 2:
                map_sub(floor002,i,j);
                break;
            case 3:
                map_sub(floor003,i,j);
                break;
            }
        }
        printf("\n");
    }
    //显示出当前面板信息,这里一会要变量一会要字符的情况下,本人认为printf比cout方便
    printf("\n生命:%d,最大生命:%d,力量:%d,灵巧:%d,护盾:%d\n",main_actors.hp,
        main_actors.maxhp,main_actors.str,main_actors.dex,main_actors.img);
    printf("黄钥匙:%d,蓝钥匙:%d,红钥匙:%d\n",main_actors.yellow,
        main_actors.blue,main_actors.red);
    return floor;
}

//处理游戏说明模块,0代表直接全是全部内容,其他值代表按一次出现一部分
void description(int choose)
{
    if(choose==0)
    {
        cout<<"本程序是根据RMXP里面我制作的,然后用C从0开始对此魔塔系统核心进行实现"<<endl;
        cout<<"所以从某种意义上来说,这个main.cpp程序并不能称作为一个游戏"<<endl;
        cout<<"好了,接下来我说明一下程序内出现的图标对应的含义"<<endl;
        cout<<endl;
        cout<<"首先地板模块也就是空格,墙就用*来进行代表"<<endl;
        cout<<endl;
        cout<<"然后怪物,由于真正的游戏里面怪物种类众多,而C++的CLI支持的图案字符很有限,"<<endl;
        cout<<"所以为了清晰,我直接使用数字编号来进行代表"<<endl;
        cout<<endl;
        cout<<"对话事件同一使用♂来进行表示"<<endl;
        cout<<endl;
        cout<<"红血瓶使用□表示,蓝血瓶使用☆表示,黄血瓶使用*表示,绿血瓶使用√表示"<<endl;
        cout<<"红宝石使用■表示,蓝宝石使用★表示,绿宝石使用§表示,黄宝石使用◎表示"<<endl;
        cout<<endl;
        cout<<"玩家控制的主角直接使用‘主’字符表示,这样清晰明了容易知道你现在在哪"<<endl;
        cout<<endl;
        cout<<"黄钥匙使用△表示,蓝钥匙使用▽表示,红钥匙使用○表示,绿钥匙使用◇表示"<<endl;
        cout<<"黄门使用▲表示,蓝门使用▼表示,红门使用●表示,绿门使用◆表示"<<endl;
        cout<<endl;
        cout<<"传送到上个地图用←表示,传送到下个地图用→表示"<<endl;
        cout<<"最后其他物品在前三层并没有,用了&表示,实际上这个程序根本没用到"<<endl;
        cout<<endl;
        cout<<"接下来,我来简单的说明魔塔的游戏玩法"<<endl;
        cout<<endl;
        cout<<"首先,黄红蓝绿钥匙分别用来开对应颜色的门,开门之后钥匙会相应的减少"<<endl;
        cout<<"最常见的是开一个门钥匙减少一个,当然还有其他的例如开某种门减3个"<<endl;
        cout<<endl;
        cout<<"第二,不同血瓶颜色对应恢复不同的血量,在本游戏开始部分红血瓶+75hp,"<<endl;
        cout<<"蓝血瓶+200hp,黄血瓶+500hp,绿血瓶+1000hp,到后面血瓶增加血量会增加"<<endl;
        cout<<endl;
        cout<<"第三,不同宝石对应加的能力不一样,在本游戏开始部分红宝石+1攻击,"<<endl;
        cout<<"蓝宝石+1防御,绿宝石+1护盾,黄宝石攻防*1.1,后面增加数值效果会增加"<<endl;
        cout<<endl;
        cout<<"第四,绿海/红海塔基本战斗机制为主角损失血量=敌人血量/(主角攻击-敌人防御)"<<endl;
        cout<<"*(敌人攻击-主角防御)-主角护盾,其中主角攻击小于敌方防御的时候"<<endl;
        cout<<",直接算主角死亡当敌人攻击小于主角防御的时候,主角即不受到伤害,当然这套"<<endl;
        cout<<"公式是在没有算上其他属性和机制的情况下,但是其他机制也是围绕这个核心的"<<endl;
        cout<<endl;
        cout<<"第五,说明一下魔塔是一个固定数值的RPG游戏,所以不存在重复刷怪的情况,"<<endl;
        cout<<"也就是说结果不存在任何的随机性,你的拆塔思路决定游戏中出现的结果"<<endl;
        cout<<endl;
        cout<<"最后说明下快捷键,按'X'键查看怪物手册,按'S'键进行存档,按'L'键进行读档"<<endl;
        cout<<"通过上下左右键控制角色的移动"<<endl;
        cout<<endl;
        cout<<"具体的基本机制了解可以参考“操作说明及所需软件\\魔塔样板7630•改”"<<endl;
        cout<<"文件夹中的Game.exe文件(太阳图标)进行了解"<<endl;
        cout<<endl;
        getch();
    }
    else
    {
        cout<<"本程序是根据RMXP里面我制作的,然后用C从0开始对此魔塔系统核心进行实现"<<endl;
        cout<<"所以从某种意义上来说,这个main.cpp程序并不能称作为一个游戏"<<endl;
        cout<<"好了,接下来我说明一下程序内出现的图标对应的含义"<<endl;
        cout<<endl;
        getch();
        cout<<"首先地板模块也就是空格,墙就用*来进行代表"<<endl;
        cout<<endl;
        getch();
        cout<<"然后怪物,由于真正的游戏里面怪物种类众多,而C++的CLI支持的图案字符很有限,"<<endl;
        cout<<"所以为了清晰,我直接使用数字编号来进行代表"<<endl;
        cout<<endl;
        getch();
        cout<<"对话事件同一使用♂来进行表示"<<endl;
        cout<<endl;
        getch();
        cout<<"红血瓶使用□表示,蓝血瓶使用☆表示,黄血瓶使用*表示,绿血瓶使用√表示"<<endl;
        cout<<"红宝石使用■表示,蓝宝石使用★表示,绿宝石使用§表示,黄宝石使用◎表示"<<endl;
        cout<<endl;
        getch();
        cout<<"玩家控制的主角直接使用‘主’字符表示,这样清晰明了容易知道你现在在哪"<<endl;
        cout<<endl;
        getch();
        cout<<"黄钥匙使用△表示,蓝钥匙使用▽表示,红钥匙使用○表示,绿钥匙使用◇表示"<<endl;
        cout<<"黄门使用▲表示,蓝门使用▼表示,红门使用●表示,绿门使用◆表示"<<endl;
        cout<<endl;
        getch();
        cout<<"传送到上个地图用←表示,传送到下个地图用→表示"<<endl;
        cout<<"最后其他物品在前三层并没有,用了&表示,实际上这个程序根本没用到"<<endl;
        cout<<endl;
        getch();
        cout<<"接下来,我来简单的说明魔塔的游戏玩法"<<endl;
        cout<<endl;
        getch();
        cout<<"首先,黄红蓝绿钥匙分别用来开对应颜色的门,开门之后钥匙会相应的减少"<<endl;
        cout<<"最常见的是开一个门钥匙减少一个,当然还有其他的例如开某种门减3个"<<endl;
        cout<<endl;
        getch();
        cout<<"第二,不同血瓶颜色对应恢复不同的血量,在本游戏开始部分红血瓶+75hp,"<<endl;
        cout<<"蓝血瓶+200hp,黄血瓶+500hp,绿血瓶+1000hp,到后面血瓶增加血量会增加"<<endl;
        cout<<endl;
        getch();
        cout<<"第三,不同宝石对应加的能力不一样,在本游戏开始部分红宝石+1攻击,"<<endl;
        cout<<"蓝宝石+1防御,绿宝石+100体力上限,黄宝石攻防*1.1,后面数值效果会增加"<<endl;
        cout<<endl;
        getch();
        cout<<"第四,绿海/红海塔基本战斗机制为主角损失血量=敌人血量/(主角攻击-敌人防御)"<<endl;
        cout<<"*(敌人攻击-主角防御)-主角护盾,其中主角攻击小于敌方防御的时候"<<endl;
        cout<<",直接算主角死亡;当敌人攻击小于主角防御的时候,主角即不受到伤害,当然这套"<<endl;
        cout<<"公式是在没有算上其他属性和机制的情况下,但是其他机制也是围绕这个核心的"<<endl;
        cout<<endl;
        getch();
        cout<<"第五,说明一下魔塔是一个固定数值的RPG游戏,所以不存在重复刷怪的情况,"<<endl;
        cout<<"也就是说结果不存在任何的随机性,你的拆塔思路决定游戏中出现的结果"<<endl;
        cout<<endl;
        getch();
        cout<<"最后说明下快捷键,按'X'键查看怪物手册,按'S'键进行存档,按'L'键进行读档"<<endl;
        cout<<"通过上下左右键控制角色的移动"<<endl;
        cout<<endl;
        getch();
        cout<<"具体的基本机制了解可以参考“操作说明及所需软件\\魔塔样板7630•改”"<<endl;
        cout<<"文件夹中的Game.exe文件(太阳图标)进行了解"<<endl;
        cout<<endl;
        getch();
    }
}

//处理光标选定的函数非本人所写,摘自https://www.cnblogs.com/coolight7/p/14792049.html
namespace coolfun
{
    //清空按键缓冲区,防止之前的残留按键行为的影响
    void kbhit_remove()
    {
        while (_kbhit())
            _getch();
    }
    
    //等待按键并读取按下按键的 ASC码
    int kbhit_wait_getasc()
    {
        int num;
        do
        {
            num = _getch();
        } while (_kbhit());
        return num;
    }
    /*光标移动函数
* x为横坐标,y为纵坐标
*/
    inline void light_gotoxy(int x, int y)
    {
        COORD pos = { (short)x,(short)y };
        HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
        SetConsoleCursorPosition(hOut, pos);
    }
    
    /*获取光标位置
    * 同时获取xy,需要传入接受的xy变量引用
    * x为横坐标,y为纵坐标
    */
    inline void light_getxy(int& x, int& y)
    {
        HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
        CONSOLE_SCREEN_BUFFER_INFO csbi;
        GetConsoleScreenBufferInfo(hConsole, &csbi);
        x = csbi.dwCursorPosition.X;
        y = csbi.dwCursorPosition.Y;
    }
    
    /*选择函数
* 返回选择的content_str[]的位置,默认1开始
* case_num:contentstr的数组大小;contentstr[]:选项内容
* >支持偏移<
*/
    template<typename T>
    int switch_case(int case_num, const T* content_str)
    {
        int nowi = 1, nowx, nowy;//nowi 记录选择的选项编号
        coolfun::light_getxy(nowx, nowy);
        coolfun::light_gotoxy(nowx, nowy + 1);
        const T* p = content_str;
        cout << ">>> " << *(p++);
        for (int i = 2; i <= case_num; ++i)  //先打印选项内容
        {
            coolfun::light_gotoxy(nowx, nowy + i);
            cout << "    " << *(p++);
        }
        coolfun::light_gotoxy(nowx, nowy + case_num + 1);
        coolfun::kbhit_remove();//清除残留按键
        for (int getnum;;) //等待按键按下,getnum记录按键asc
        {
            getnum = coolfun::kbhit_wait_getasc();
            coolfun::light_gotoxy(nowx, nowy + nowi); //移动到原来的编号选项前
            cout << "   ";  //覆盖掉它的">>>"
            switch (getnum) //获取按下的按键的ask2值
            {
                case 72: //上
                {
                    if (nowi > 1)   //在第一个再按上键会到最后一个
                        --nowi;
                    else
                        nowi = case_num;
                }break;
                case 80: //下
                {
                    if (nowi < case_num)  //在最后一个按下键会到第一个
                        ++nowi;
                    else
                        nowi = 1;
                }break;
                case 13:  //Enter键确定
                {
                    coolfun::light_gotoxy(nowx, nowy + nowi);
                    cout << "-->";
                    coolfun::light_gotoxy(nowx, nowy + case_num + 1);
                    return nowi;  //返回当前选项编号
                }break;
            }
            coolfun::light_gotoxy(nowx, nowy + nowi);//移动到修改后的位置
            cout << ">>>";
            coolfun::light_gotoxy(nowx, nowy + case_num + 1); //把光标移动回输出的最后
        }
        return 0;
    }
    
}

//魔塔战斗机制的描述
void battle(int id)
{
    /*这里相当于将地图表和对应战斗怪物表结合了起来,这个实现操作就是数据库join语句,
    类似于select * from floorXXX join enemy_data on floorXXX.sub=enemy_data.id*/
    //敌人攻击<=主角防御
    if(enemy_data[id].str<=main_actors.dex)
    {
        main_actors.hp-=0;
    }
    //主角攻击<=敌人防御
    else if(main_actors.str<=enemy_data[id].dex)
    {
        main_actors.hp-=pow(2,31);
    }
    //正常情况下,主角血量-=敌人血量/(主角攻击-敌人防御)*(敌人攻击-主角防御)-主角护盾
    //但是要注意如果正好整除情况下,也就是你最后一下把对面击败,这回合并不会扣血
    else
    {
        //正好整除的情况下
        if(enemy_data[id].hp%(main_actors.str-enemy_data[id].dex)==0)
        {
            //魔塔基本机制里面不存在负数伤害,所以小于0时也就不扣血
            if((enemy_data[id].hp/(main_actors.str-enemy_data[id].dex)
                -1)*(enemy_data[id].str-main_actors.dex)-main_actors.img<0)
            {
                main_actors.hp-=0;
                goto a;
            }
            main_actors.hp-=(enemy_data[id].hp/(main_actors.str-enemy_data[id].dex)
            -1)*(enemy_data[id].str-main_actors.dex)-main_actors.img;
        }
        //其他情况下
        else
        {
            if(enemy_data[id].hp/(main_actors.str-enemy_data[id].dex)*
                (enemy_data[id].str-main_actors.dex)-main_actors.img<0)
            {
                main_actors.hp-=0;
                goto a;
            }
            main_actors.hp-=enemy_data[id].hp/(main_actors.str-enemy_data[id].dex)*
            (enemy_data[id].str-main_actors.dex)-main_actors.img;
        }
    }
    a:
    //当生命值不为正数时候游戏失败
    if(main_actors.hp<=0)
    {
        //设置字体为红色
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED);
        system("cls");
        cout<<endl;
        cout<<setw(30)<<"你被怪物打死了!你输了!!!"<<endl;
        cout<<setw(21)<<"真的是太逊了!!!"<<endl;
        exit(0);
    }
}

//对话事件的处理
void undefined_1(int id)
{
    switch(id)
    {
    case 1:
        cout<<"这是第一个对话事件";
        break;
    case 2:
        cout<<"这是第二个对话事件";
        break;
    case 3:
        cout<<"测试使用";
        break;
    /*case XXX:
        相应的事件处理;
        break;
    不过我这个并非是一个完美的处理方法,在RMXP本质上是通过NPC编号和这个
    角色第几个对话框进行绑定的.此方法如果事件不超过1万个问题不大,也就是说小规模
    塔用这种方法没问题,大规模的容易导致游戏卡.此功能实现繁琐而且时间有限就算了
    */
    }
}

//处理碰到血瓶时候恢复的血量
void blood_vial(int id)
{
    /*这里采用的是恢复血量*倍数,方便之后血瓶效果的变化,例如打了10层后血瓶恢复
    血量翻倍等,这也是魔塔样板自带可通过GUI进行的设置,这样提高了程序的可扩展性*/
    switch(id)
    {
    case 1:
        main_actors.hp+=75*multi;
        break;
    case 2:
        main_actors.hp+=200*multi;
        break;
    case 3:
        main_actors.hp+=500*multi;
        break;
    case 4:
        main_actors.hp+=1000*multi;
        break;
    }
    //由于这个魔塔我设置了体力上限,所以当增加血量超过了体力上限,要重置血量为体力上限
    if(main_actors.hp>main_actors.maxhp)
    {
        main_actors.hp=main_actors.maxhp;
    }
}

//处理碰到宝石时候增加相应的能力值
void gem_stone(int id)
{
    //和处理血瓶一样要考虑到可扩展性
    switch(id)
    {
    case 1:
        main_actors.str+=1*multi;
        break;
    case 2:
        main_actors.dex+=1*multi;
        break;
    case 3:
        main_actors.maxhp+=100*multi;
        break;
    case 4:
        main_actors.str*=1.1;
        main_actors.dex*=1.1;
        break;
    }
}


//处理碰到钥匙后添加相应的钥匙
void get_keys(int id)
{
    /*获得钥匙则相应钥匙+1,后期有钥匙串要额外写成新的事件,这个没法用倍率表示,
    因为不是一个恒定的变量*/
    switch(id)
    {
    case 1:
        main_actors.yellow+=1;
        break;
    case 2:
        main_actors.blue+=1;
        break;
    case 3:
        main_actors.red+=1;
        break;
    case 4:
        main_actors.green+=1;
        break;
    }
}

//处理无法打开时,进行的撤回操作
void cant_door(int *action,int *x,int *y)
{
    switch(*action)
    {
    case 1:
        *x+=1;
        break;
    case 2:
        *x-=1;
        break;
    case 3:
        *y+=1;
        break;
    case 4:
        *y-=1;
        break;
    }
}

//处理开门的事件
int open_door(int id,int *action,int *x,int *y)
{
    /*这个也是基本的4类门,如果需要多个钥匙的,需要重新写事件.这里和前面不同的是,
    如果钥匙不够就和墙一样无法通行,也就是和碰墙一样做相应的撤回*/
    switch(id)
    {
    case 1:
        //返回相应的没有钥匙的值
        if(main_actors.yellow<=0)
        {
            cant_door(action,x,y);
            return NO_YELLOW;
        }
        else
        {
            main_actors.yellow-=1;
        }
        break;
    case 2:
        if(main_actors.blue<=0)
        {
            cant_door(action,x,y);
            return NO_BLUE;
        }
        else
        {
            main_actors.blue-=1;
        }
        break;
    case 3:
        if(main_actors.red<=0)
        {
            cant_door(action,x,y);
            return NO_RED;
        }
        else
        {
            main_actors.red-=1;
        }
        break;
    case 4:
        if(main_actors.green<=0)
        {
            cant_door(action,x,y);
            return NO_GREEN;
        }
        else
        {
            main_actors.green-=1;
        }
        break;
    }
    return 1;
}

//上下楼的切换
int up_down(int floor,int id)
{
    //判断当前在几楼
    switch(floor)
    {
    case 1:
        //判断是上楼还是下楼(切换上个地图还是下个地图)
        switch(id)
        {
        case 2:
            floor+=1;
            floor002[5][12].info=6;
            floor002[5][12].sub=0;
            break;
        }
        break;
    case 2:
        switch(id)
        {
        case 1:
            floor-=1;
            floor001[5][12].info=6;
            floor001[5][12].sub=0;
            break;
        case 2:
            floor+=1;
            floor003[2][2].info=6;
            floor003[2][2].sub=0;
            break;
        }
        break;
    case 3:
        switch(id)
        {
        case 1:
            floor-=1;
            floor002[2][2].info=6;
            floor002[2][2].sub=0;
            break;
        case 2:
            //设置字体为绿色
            SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN);
            system("cls");
            cout<<endl;
            cout<<"CLI界面的测试版核心功能结束!"<<endl;
            cout<<"恭喜你通关了,真正的游戏GUI界面体验请打开游戏文件(RMXP工具实现)"<<endl;
            cout<<"文件夹中的Game.exe(太阳图标)"<<endl;
            exit(0);
        }
        break;
    }
    return floor;
}

//刷新第1-10层传送事件
void updown_refresh01()
{
    floor001[5][13].info=9;
    floor001[5][13].sub=2;
    floor002[5][13].info=9;
    floor002[5][13].sub=1;
    floor002[2][1].info=9;
    floor002[2][1].sub=2;
    floor003[2][1].info=9;
    floor003[2][1].sub=1;
    floor003[12][3].info=9;
    floor003[12][3].sub=2;
}

/*备注:其实在JS,Py等语言里面使用嵌套定义函数是很容易的事情,但是C/C++函数不允许嵌套定义,
只能通过地址传值的方法来进行传递,注意点就是小心使用指针层级关系*/
//根据不同的楼层选择不同的楼层地图
int floor_sub(int *action,int *x,int *y,int f,mota floor[][MAP_L])
{
    int tips=0;
    //将原来主角的位置置为0,前提是没有碰到墙
    if(floor[*x][*y].info!=1)
    {
        switch(*action)
        {
        case 1:
        //将原来是主角的点重新变为地板
            floor[*x+1][*y].info=0;
            floor[*x+1][*y].sub=0;
            break;
        case 2:
            floor[*x-1][*y].info=0;
            floor[*x-1][*y].sub=0;
            break;
        case 3:
            floor[*x][*y+1].info=0;
            floor[*x][*y+1].sub=0;
            break;
        case 4:
            floor[*x][*y-1].info=0;
            floor[*x][*y-1].sub=0;
            break;
        }
    }
    //如果行走中碰到墙也就是不走动,相当于对原本走动情况下做相反的动作
    switch(floor[*x][*y].info)
    {
    case 1:
        switch(*action)
        {
        case 1:
            *x+=1;
            break;
        case 2:
            *x-=1;
            break;
        case 3:
            *y+=1;
            break;
        case 4:
            *y-=1;
            break;
        }
        break;
    //当主角碰到怪物时候的战斗处理
    case 2:
        battle(floor[*x][*y].sub);
        break;
    //对话事件处理
    case 3:
        //由于前三层不涉及对话人物推进剧情,此函数不会被执行到
        undefined_1(floor[*x][*y].sub);
        break;
    //血瓶处理
    case 4:
        blood_vial(floor[*x][*y].sub);
        break;
    //宝石处理
    case 5:
        gem_stone(floor[*x][*y].sub);
        break;
    //钥匙处理
    case 7:
        get_keys(floor[*x][*y].sub);
        break;
    //开门处理
    case 8:
        //这里必须要传送动作和坐标,门无法打开时撤回操作需要对动作和坐标进行判断
        tips=open_door(floor[*x][*y].sub,action,x,y);
        break;
    //切换地图处理
    case 9:
        /*这里直接手工指定每个楼层直接定位就行,使用for循环遍历只有使得时间复杂度提高,
        规模一大很容易卡顿*/
        f=up_down(f,floor[*x][*y].sub);
    }
    system("cls");
    //新的点作为主角所在点,然后重新刷新地图
    floor[*x][*y].info=6;
    floor[*x][*y].sub=0;
    //每10层上下楼刷新图标,这样可以有效预防规模大的时候卡顿(虽然这里用不到)
    //而且这里不适合if..else if结构,因为这样会增加寻找次数
    switch(f/10)
    {
    case 0:
        updown_refresh01();
        break;
    }
    maps(f);
    //其实在游戏里面前5个战斗层都为入学测试,这里为了方便查看楼层是否切换,表示为第几层
    printf("当前你所在楼层为:%d层\n",f);
    /*其实魔塔里面钥匙不够并不会提示,因为GUI界面很清晰的看到门颜色,
    无需提示,但CLI界面看着不清晰所以我额外增加了提示*/
    switch(tips)
    {
    case NO_YELLOW:
        printf("\n你的黄钥匙不够,无法开门\n");
        break;
    case NO_BLUE:
        printf("\n你的蓝钥匙不够,无法开门\n");
        break;
    case NO_RED:
        printf("\n你的红钥匙不够,无法开门\n");
        break;
    case NO_GREEN:
        printf("\n你的绿钥匙不够,无法开门\n");
        break;
    }
    return f;
}

//再次将指针传递给move_sub函数处理楼层
int move_sub(int action,int *x,int *y,int floor)
{
    switch(floor)
    {
    case 1:
        floor=floor_sub(&action,x,y,floor,floor001);
        break;
    case 2:
        floor=floor_sub(&action,x,y,floor,floor002);
        break;
    case 3:
        floor=floor_sub(&action,x,y,floor,floor003);
        break;
    }
    return floor;
}

//处理游戏中每步移动后的情景
//其中参数action表示移动结果,1代表上,2代表下,3代表左,4代表右
int mota_move(int action,int *x,int *y,int floor)
{
    switch(action)
    {
    //处理按键盘上键触发的事件
    case 1:
        *x-=1;
        floor=move_sub(1,x,y,floor);
        break;
    //处理按键盘下键触发的事件
    case 2:
        *x+=1;
        floor=move_sub(2,x,y,floor);
        break;
    //处理按键盘左键触发的事件
    case 3:
        *y-=1;
        floor=move_sub(3,x,y,floor);
        break;
    //处理按键盘右键触发的事件
    case 4:
        *y+=1;
        floor=move_sub(4,x,y,floor);
        break;
    }
    return floor;
}

//丢失血量其实和战斗过程处理方法类似
string lose_hp(int id)
{
    int tmp;
    if(enemy_data[id].str<=main_actors.dex)
    {
        return "0";
    }
    //主角攻击<=敌人防御
    else if(main_actors.str<=enemy_data[id].dex)
    {
        return "????";
    }
    //正常情况下,主角血量-=敌人血量/(主角攻击-敌人防御)*(敌人攻击-主角防御)-主角护盾
    //但是要注意如果正好整除情况下,也就是你最后一下把对面击败,这回合并不会扣血
    else
    {
        //正好整除的情况下
        if(enemy_data[id].hp%(main_actors.str-enemy_data[id].dex)==0)
        {
            //魔塔基本机制里面不存在负数伤害,所以小于0时也就不扣血
            if((enemy_data[id].hp/(main_actors.str-enemy_data[id].dex)
                -1)*(enemy_data[id].str-main_actors.dex)-main_actors.img<0)
            {
                return "0";
            }
            tmp=(enemy_data[id].hp/(main_actors.str-enemy_data[id].dex)
                -1)*(enemy_data[id].str-main_actors.dex)-main_actors.img;
            return to_string(tmp);
        }
        //其他情况下
        else
        {
            if(enemy_data[id].hp/(main_actors.str-enemy_data[id].dex)*
                (enemy_data[id].str-main_actors.dex)-main_actors.img<0)
            {
                return "0";
            }
            tmp=main_actors.hp-=enemy_data[id].hp/(main_actors.str-enemy_data[id].dex)*
            (enemy_data[id].str-main_actors.dex)-main_actors.img;
            return to_string(tmp);
        }
    }
}

//将所有可能出现的怪物用switch...case的方法列出来
void monster_sub(int id)
{
    cout<<enemy_data[id].name<<"\t\t编号:"<<id<<",生命:"<<enemy_data[id].hp<<endl;
    cout<<"攻击:"<<enemy_data[id].str<<",防御:"<<enemy_data[id].dex<<",金币:"<<
    enemy_data[id].gold<<",经验:"<<enemy_data[id].exp<<",伤害:"<<lose_hp(id)<<endl;
    cout<<endl;
}

//怪物手册根据楼层进行具体的处理
int manual_sub(int f,mota floor[][MAP_L])
{
    //通常一个楼层里面怪物数量不超过50;
    int item[MAX_M],count=0;
    /*这里看似3层循环,但是因为一般楼层地图都是15*15,然后怪物数量一般为10以下,
    循环执行次数所以一般不超过2000次*/
    for(int i=0;i<MAP_L;i++)
    {
        for(int j=0;j<MAP_L;j++)
        {
            //地图上的点,查看是否有相应相应类型的怪物,加入到item中
            if(floor[i][j].info==2)
            {
                item[count]=floor[i][j].sub;
                count++;
            }
        }
    }
    //排序并且进行去重操作
    sort(item,item+count);
    int n=unique(item,item+count)-item;
    system("cls");
    //遍历本层出现的怪物,进行查看
    for(int i=0;i<n;i++)
    {
        monster_sub(item[i]);
    }
    char ch=getch();
    switch(ch)
    {
        case 'x':    case 'X':
            system("cls");
            maps(f);
            printf("当前你所在楼层为:%d层\n",f);
            break;
    }
    return f;
}
//怪物手册处理
int monster_manual(int floor)
{
    switch(floor)
    {
    case 1:
        floor=manual_sub(floor,floor001);
        break;
    case 2:
        floor=manual_sub(floor,floor002);
        break;
    case 3:
        floor=manual_sub(floor,floor003);
        break;
    }
    return floor;
}

//对相应层进行存档的具体处理
void save_sub(mota floor[][MAP_L],FILE *fp)
{
    //所谓的存档就是将当前地图信息保存下来
    for(int i=0;i<MAP_L;i++)
    {
        for(int j=0;j<MAP_L;j++)
        {
            fprintf(fp,"%d,%d ",floor[i][j].info,floor[i][j].sub);
        }
        fprintf(fp,"\n");
    }
}

//判断处于什么层,并对每层进行存档
void save_floor(int floor,FILE *fp)
{
    switch(floor)
    {
    case 1:
        save_sub(floor001,fp);
        break;
    case 2:
        save_sub(floor002,fp);
        break;
    case 3:
        save_sub(floor003,fp);
        break;
    }
}

//存档功能实现
void saving_game(int floor)
{
    int n;
    char save[4],mosa[16]="motaSave",txt[5]=".txt";
    FILE *fp;
    system("cls");
    cout<<"你需要存到第几个位置?(1-100):";
    cin>>n;
    if(n<1 || n>100)
    {
        cout<<"没有相应的位置,无法存档,按任意键返回游戏"<<endl;
        system("pause");
    }
    else
    {
        //数字转换为字符串后,然后与mosa连接,之后mosa再和后缀名连接
        itoa(n,save,10);
        strcat(mosa,save);
        fp=fopen(strcat(mosa,txt),"w");
        if(fp==NULL)
        {
            cout<<"当前文件夹没有写入权限,请开启文件夹的写权限!"<<endl;
        }
        for(int i=1;i<=CURRENT_FLOOR;i++)
        {
            save_floor(i,fp);
        }
        //不要忘记保存主角当前状态,因为这是个变量
        fprintf(fp,"%8d%8d%8d%8d%8d%8d%8d%8d%8d%8d%8d%8d%8d%8d%8d",main_actors.level,
        main_actors.hp,main_actors.maxhp,main_actors.sp,main_actors.maxsp,
        main_actors.str,main_actors.dex,main_actors.img,main_actors.agi,
        main_actors.gold,main_actors.exp,main_actors.yellow,main_actors.blue,
        main_actors.red,main_actors.green);
        fclose(fp);
    }
    system("cls");
    maps(floor);
    printf("当前你所在楼层为:%d层\n",floor);
}

//对相应层进行读档的具体处理
void load_sub(mota floor[][MAP_L],FILE *fp)
{
    //所谓的存档就是将当前地图信息保存下来
    for(int i=0;i<MAP_L;i++)
    {
        for(int j=0;j<MAP_L;j++)
        {
            fscanf(fp,"%d,%d ",&floor[i][j].info,&floor[i][j].sub);
        }
    }
}

//判断处于什么层,并对每层进行读档
void load_floor(int floor,FILE *fp)
{
    switch(floor)
    {
    case 1:
        load_sub(floor001,fp);
        break;
    case 2:
        load_sub(floor002,fp);
        break;
    case 3:
        load_sub(floor003,fp);
        break;
    }
}

//对每层进行搜索,寻找主角的位置
void poistion_sub(mota floor[][MAP_L],int *x,int *y)
{
    //寻找位置,如果找到将对应二维数组下标赋值给x,y坐标轴
    for(int i=0;i<MAP_L;i++)
    {
        for(int j=0;j<MAP_L;j++)
        {
            if(floor[i][j].info==6)
            {
                *x=i;
                *y=j;
                goto a;
            }
        }
    }
    a:;
}

//判断处于什么层,并对每层进行位置搜索
void get_poistion(int floor,int *x,int *y)
{
    switch(floor)
    {
    case 1:
        poistion_sub(floor001,x,y);
        break;
    case 2:
        poistion_sub(floor002,x,y);
        break;
    case 3:
        poistion_sub(floor003,x,y);
        break;
    }
}

//读档功能实现,读档后因为原来的主角位置会更改,必须对其重新定位
int loading_game(int floor,int *x,int *y)
{
    int n;
    char load[4],mosa[16]="motaSave",txt[5]=".txt";
    FILE *fp;
    system("cls");
    cout<<"你需要读第几个位置?(1-100):";
    cin>>n;
    //数字转换为字符串后,然后与mosa连接,之后mosa再和后缀名连接
    itoa(n,load,10);
    strcat(mosa,load);
    fp=fopen(strcat(mosa,txt),"r");
    if(fp==NULL)
    {
        cout<<"此位置没有相应存档,无法读档,按任意键返回游戏"<<endl;
        system("pause");
    }
    else
    {
        for(int i=1;i<CURRENT_FLOOR;i++)
        {
            load_floor(i,fp);
        }
        /*由于fscanf是不记录换行符的,所以需要换个思路.之前通过固定长度保存主角信息,
        然后就可以通过fseek将指针跳转到末尾然后往前移动8*数据项即可*/
        fseek(fp,-120,SEEK_END);
        fscanf(fp,"%8d%8d%8d%8d%8d%8d%8d%8d%8d%8d%8d%8d%8d%8d%8d",&main_actors.level,
        &main_actors.hp,&main_actors.maxhp,&main_actors.sp,&main_actors.maxsp,
        &main_actors.str,&main_actors.dex,&main_actors.img,&main_actors.agi,
        &main_actors.gold,&main_actors.exp,&main_actors.yellow,&main_actors.blue,
        &main_actors.red,&main_actors.green);
        //寻找主角当前位置,找到后将相应坐标轴赋值给x,y;
        for(int i=1;i<CURRENT_FLOOR;i++)
        {
            get_poistion(i,x,y);
        }
    }
    system("cls");
    //游戏开始后才会执行地图刷新,或者找到对应的读档,调入相应读档
    if(start==1 ||(start==0 && fp!=NULL))
    {
        maps(floor);
        printf("当前你所在楼层为:%d层\n",floor);
        start=1;
        return 1;
    }
    fclose(fp);
    return 0;
}

int main(void)
{
    int choose,sub_choose1,floor=1,x,y,res;
    char ch;
    //处理游戏开始界面设计
    a:
    cout<<"****************************************"<<endl;
    cout<<"*"<<setw(38)<<" "<<"*"<<endl;
    cout<<"*"<<setw(38)<<"龙族(Dragon Raja)           "<<"*"<<endl;
    cout<<"*"<<setw(38)<<" "<<"*"<<endl;
    cout<<"****************************************"<<endl;
    string str[] =
    {
        "龙的世界",
        "传奇继续",
        "退出游戏",
        "游戏说明",
    };
    //通过coolfun命名空间内的子函数返回的值判断选择第几项
    choose=coolfun::switch_case(4, str);
    //这里加载完初始地图后,设置主角初始位置后,跳转到游戏开始处
    if(choose==1)
    {
        x=13,y=7;
        system("cls");
        floor001[x][y].info=6;
        floor001[x][y].sub=0;
        maps(floor);
        //游戏开始标记
        start=1;
        cout<<"当前你所在楼层为:1层";
    }
    else if(choose==2)
    {
        res=loading_game(floor,&x,&y);
        if(!res)
        {
            goto a;
        }
    }
    //退出游戏
    else if(choose==3)
    {
        exit(0);
    }
    //游戏说明
    else if(choose==4)
    {
        system("cls");
        cout<<"是否需要直接显示说明?(0代表一次行显示完毕,其他代表按一次出现一部分)";
        cin>>sub_choose1;
        system("cls");
        description(sub_choose1);
        system("cls");
        //显示完毕后回到开始处
        goto a;
    }
    //在计算机中所谓的动态本质是重新刷新屏幕内容,这里就是每走一步刷新一次
    while(true)
    {
        ch=getch();
        if (ch==-32)
        {
            ch=getch();
            switch (ch)
            {
            case UP:
                //这里必须使用地址,通过传递地址方法来改变主函数横坐标纵坐标
                floor=mota_move(1,&x,&y,floor);
                break;
            case DOWN:
                floor=mota_move(2,&x,&y,floor);
                break;
            case LEFT:
                floor=mota_move(3,&x,&y,floor);
                break;
            case RIGHT:
                floor=mota_move(4,&x,&y,floor);
                break;
            default:
                break;
            }
        }
        switch(ch)
        {
        //按'X'键调出怪物手册查看
        case 'x':    case 'X':
            floor=monster_manual(floor);
            break;
        //按'S'键进行存档,按'L'键进行读档
        case 's':    case 'S':
            saving_game(floor);
            break;    
        case 'l':    case 'L':
            loading_game(floor,&x,&y);
            break;
        }
    }
}

test.cpp(魔塔中怪物特殊属性简易实现)

using namespace std;
/*由于是伪代码做大概的实现,会大量使用全局变量,怎么方便怎么来,不考虑程序健壮性,
可扩展性等.在真正的程序中这样做容易因为冲突造成bug的,我游戏主文件main.cpp就没
这么来.后面的注释会比较少,因为和主文件相同,具体含义对照主文件main.cpp*/
int x=1,y=1;
char ch;
int zhongdu=0,suairuo=0,jiangu=3,xiangong=4,lianji=1;
int wudi=1,wudinum=8,mofang=9,chouhen=0;
typedef struct
{
    int id;
    int hp;
    int str;
    int dex;
    int gold;
    int exp;
    int pro;
    //怪物拥有的状态,0为没有状态,其实可以有多个状态,由于采用伪代码无需考虑
    int state;
    char name[20];
}enemy_test;
actors main_tmp={
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};
enemy_test enemy_data[AMOUNT_TEST]={
    {0,66,1,1,1,0,0,1,"中毒怪"},
    {1,66,1,1,1,0,0,2,"衰弱怪"},
    {2,66,1,1,1,0,0,3,"吸血怪"},
    {3,66,850,1,1,0,0,4,"坚固怪"},
    {4,66,888,1,1,0,0,5,"先攻怪"},
    {5,999,9999,1,1,0,0,6,"魔攻怪"},
    {6,999,9999,1,1,0,0,7,"连击怪"},
    {7,66,1,1,1,0,0,8,"破甲怪"},
    {8,66,1,1,1,0,0,9,"无敌怪"},
    {9,9999,1,1,1,0,0,10,"模仿怪"},
    {10,66,1,1,1,0,0,11,"反击怪"},
    {11,66,1,1,1,0,0,12,"净化怪"},
    {12,66,1,1,1,0,0,13,"自爆怪"},
    {13,66,1,1,1,0,0,14,"退化怪"},
    {14,66,1,1,1,0,0,15,"仇恨怪"}
};
mota floortest[MAP_TEST][MAP_TEST]={
    {{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0}},
    {{1,0},{6,0},{0,0},{0,0},{0,0},{2,0},{0,0},{0,0},{0,0},{1,0}},
    {{1,0},{0,0},{2,1},{0,0},{0,0},{0,0},{2,2},{0,0},{0,0},{1,0}},
    {{1,0},{0,0},{0,0},{2,3},{0,0},{0,0},{0,0},{2,4},{0,0},{1,0}},
    {{1,0},{0,0},{0,0},{0,0},{2,5},{0,0},{0,0},{0,0},{2,6},{1,0}},
    {{1,0},{2,7},{0,0},{0,0},{0,0},{2,8},{0,0},{0,0},{0,0},{1,0}},
    {{1,0},{0,0},{2,9},{0,0},{0,0},{0,0},{2,10},{0,0},{0,0},{1,0}},
    {{1,0},{0,0},{0,0},{2,11},{0,0},{0,0},{0,0},{2,12},{0,0},{1,0}},
    {{1,0},{0,0},{0,0},{0,0},{2,13},{0,0},{0,0},{0,0},{2,14},{1,0}},
    {{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0}}
};

//由于这里仅做怪物属性测试,这里把原来的很多功能砍掉了,具体主文件map_sub
void maps()
{
    for(int i=0;i<MAP_TEST;i++)
    {
        for(int j=0;j<MAP_TEST;j++)
        {
            switch(floortest[i][j].info)
            {
            case 0:
                cout<<setw(3)<<" ";
                break;
            case 1:
                cout<<setw(3)<<"*";
                break;
            case 2:
                for(int k=0;k<AMOUNT_TEST;k++)
                {
                    if(k==floortest[i][j].sub)
                    {
                        cout<<setw(3)<<floortest[i][j].sub;
                        break;
                    }
                }
                break;
            case 6:
                cout<<setw(3)<<"主";
                break;
            }
        }
        printf("\n");
    }
    printf("\n生命:%d,最大生命:%d,力量:%d,灵巧:%d,护盾:%d\n",main_actors.hp,
        main_actors.maxhp,main_actors.str,main_actors.dex,main_actors.img);
}

//处理打怪时候碰到的怪物属性处理
void enemy_state(int state,int count)
{
    switch(state)
    {
    //中毒属性,每走一步损失一定血量
    case 1:
        zhongdu=1;
        break;
    //衰弱状态,使得主角攻防和护盾损失一定比例,这里设定为减少25%
    case 2:
        if(!suairuo && count==1)
        {
            main_tmp.str=main_actors.str*0.75;
            main_tmp.dex=main_actors.dex*0.75;
            main_tmp.img=main_actors.img*0.75;
            suairuo=1;
        }
        else if(suairuo && count==2)
        {
            main_actors.str=main_tmp.str;
            main_actors.dex=main_tmp.dex;
            main_actors.img=main_tmp.img;
        }
        break;
    //吸血状态,战斗开始时吸取一定体力,这里设定吸取主角血量的30%
    case 3:
        if(count==1)
        {
            main_actors.hp*=0.7;
        }
        break;
    //坚固状态,意思是当怪物防御<勇士攻击的时候,怪物防御变为勇士攻击-1
    case 4:
        if(enemy_data[jiangu].dex+1<main_actors.str)
        {
            enemy_data[jiangu].dex=main_actors.str-1;
        }
        break;
    //先攻状态,所谓先攻就是怪物首先攻击一次,加一次对应值伤害就行
    case 5:
        if(enemy_data[xiangong].str<=main_actors.dex)
        {
            main_actors.hp-=0;
        }
        else
        {
            main_actors.hp-=enemy_data[xiangong].str-main_actors.dex;
        }
        break;
    //魔攻状态,所谓魔攻就是无视对手防御,相当于主角防御为0
    case 6:
        if(count==1)
        {
            main_tmp.dex=main_actors.dex;
            main_actors.dex=0;
        }
        else if(count==2)
        {
            main_actors.dex=main_tmp.dex;
        }
        break;
    //连击状态就是判断每回合多攻击n-1次
    case 7:
        if(count==1)
        {
            lianji=2;
        }
        else if(count==2)
        {
            lianji=1;
        }
        break;
    //破甲状态判定为对主角造成主角防御*倍数,这里设定倍数为1
    case 8:
        if(count==1)
        {
            main_actors.hp-=main_actors.dex;
        }
        break;
    //无敌状态,就是主角在没有获得相应道具时无法击败该怪物
    case 9:
        if(wudi==1 && count==1)
        {
            main_tmp.str=enemy_data[wudinum].str;
            main_tmp.dex=enemy_data[wudinum].dex;
            enemy_data[wudinum].str=9999999;
            enemy_data[wudinum].dex=9999999;
        }
        else if(wudi==1 && count==2)
        {
            enemy_data[wudinum].str=main_tmp.str;
            enemy_data[wudinum].dex=main_tmp.dex;
        }
        break;
    //模仿状态,当怪物的攻击或防御小于主角时,复制其属性
    case 10:
        if(enemy_data[mofang].str<main_actors.str)
        {
            enemy_data[mofang].str=main_actors.str;
        }
        if(enemy_data[mofang].dex<main_actors.dex)
        {
            enemy_data[mofang].dex=main_actors.dex;
        }
        break;
    //反击状态判定为对主角造成主角攻击*倍数,这里设定倍数为1
    case 11:
        if(count==1)
        {
            main_actors.hp-=main_actors.str;
        }
        break;
    //净化状态判定为对主角造成主角护盾*倍数,这里设定倍数为1
    case 12:
        if(count==1)
        {
            main_actors.hp-=main_actors.img;
        }
        break;
    //自爆状态的意思为战斗后将主角的血量变为1
    case 13:
        if(count==2)
        {
            main_actors.hp=1;
        }
        break;
    //退化状态,战斗后使得主角攻防和护盾相应减少,这里设定攻防-3,护盾-20
    case 14:
        if(count==2)
        {
            main_actors.str-=3;
            main_actors.dex-=3;
            main_actors.img-=20;
        }
        break;
    //仇恨状态,额外增加仇恨值作为伤害,然后/2.仇恨值来源这里设定每打一个怪物+3
    case 15:
        if(count==1)
        {
            main_actors.hp-=chouhen;
            chouhen/=2;
        }
        break;
    }
}

//这里是抽象级别高的伪代码,就是大概表示状态在魔塔里面如何消除
/*
if(use_item(解毒药水)){
    zhongdu(中毒状态)=0;
}
if(use_item(解衰药水)){
    suairuo(衰弱状态)=0;
    main_actors.str*=1.333333;
    main_actors.dex*=1.333333;
    main_actors.img*=1.333333;
if(use_item(圣十字架)){
    wudi(无敌状态)=0;
}
*/

//对应主文件battle
void battle_test(int id)
{
    enemy_state(enemy_data[id].state,1);
    //连击次数决定执行几次
    for(int i=0;i<lianji;i++)
    {
        if(enemy_data[id].str<=main_actors.dex)
        {
            main_actors.hp-=0;
        }
        else if(main_actors.str<=enemy_data[id].dex)
        {
            main_actors.hp-=9999999;
        }
        else
        {
            if(enemy_data[id].hp%(main_actors.str-enemy_data[id].dex)==0)
            {
                if((enemy_data[id].hp/(main_actors.str-enemy_data[id].dex)
                    -1)*(enemy_data[id].str-main_actors.dex)-main_actors.img<0)
                {
                    main_actors.hp-=0;
                    goto a;
                }
                main_actors.hp-=(enemy_data[id].hp/(main_actors.str-enemy_data[id].dex)
                    -1)*(enemy_data[id].str-main_actors.dex)-main_actors.img;
            }
            else
            {
                if(enemy_data[id].hp/(main_actors.str-enemy_data[id].dex)*
                    (enemy_data[id].str-main_actors.dex)-main_actors.img<0)
                {
                    main_actors.hp-=0;
                    goto a;
                }
                main_actors.hp-=enemy_data[id].hp/(main_actors.str-enemy_data[id].dex)*
                (enemy_data[id].str-main_actors.dex)-main_actors.img;
            }
        }
    }
    chouhen+=3;
    enemy_state(enemy_data[id].state,2);
    a:
    if(main_actors.hp<=0)
    {
        system("cls");
        cout<<"无敌怪确实无敌,测试成功"<<endl;
        exit(0);
    }
}

//对应主文件floor_sub,也是砍掉了很多处理
void floor_test(int *action)
{
    int tips=0;
    if(floortest[x][y].info!=1)
    {
        switch(*action)
        {
        case 1:
            floortest[x+1][y].info=0;
            floortest[x+1][y].sub=0;
            break;
        case 2:
            floortest[x-1][y].info=0;
            floortest[x-1][y].sub=0;
            break;
        case 3:
            floortest[x][y+1].info=0;
            floortest[x][y+1].sub=0;
            break;
        case 4:
            floortest[x][y-1].info=0;
            floortest[x][y-1].sub=0;
            break;
        }
    }
    switch(floortest[x][y].info)
    {
    case 1:
        switch(*action)
        {
        case 1:
            x+=1;
            break;
        case 2:
            x-=1;
            break;
        case 3:
            y+=1;
            break;
        case 4:
            y-=1;
            break;
        }
        break;
    case 2:
        battle_test(floortest[x][y].sub);
        break;
    }
    system("cls");
    floortest[x][y].info=6;
    floortest[x][y].sub=0;
    //中毒的时候每走1步掉血,这里设定为10;
    if(zhongdu)
    {
        main_actors.hp-=10;
    }
    maps();
}

//对应主文件mota_move
void mota_move(int action)
{
    switch(action)
    {
    case 1:
        x-=1;
        floor_test(&action);
        break;
    case 2:
        x+=1;
        floor_test(&action);
        break;
    case 3:
        y-=1;
        floor_test(&action);
        break;
    case 4:
        y+=1;
        floor_test(&action);
        break;
    }
}
int main(void)
{
    main_actors.hp=114514;
    main_actors.maxhp=1919810;
    main_actors.str=888;
    main_actors.dex=666;
    main_actors.img=8848;
    maps();
    while(true)
    {
        ch=getch();
        if (ch==-32)
        {
            ch=getch();
            switch (ch)
            {
            case UP:
                mota_move(1);
                break;
            case DOWN:
                mota_move(2);
                break;
            case LEFT:
                mota_move(3);
                break;
            case RIGHT:
                mota_move(4);
                break;
            default:
                break;
            }
        }
    }
    return 0;
}
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我可以为您提供一个简单的魔塔游戏C语言代码示例,但是需要注意的是,这只是一个基础版本,可能不具备完整的游戏体验。 ```c #include <stdio.h> #include <stdlib.h> #include <time.h> #define MAP_SIZE 15 #define LEVEL_NUM 10 int map[MAP_SIZE][MAP_SIZE]; int level = 1; int hp = 100; int attack = 10; void init_map() { int i, j; for (i = 0; i < MAP_SIZE; i++) { for (j = 0; j < MAP_SIZE; j++) { if (i == 0 || i == MAP_SIZE - 1 || j == 0 || j == MAP_SIZE - 1) { map[i][j] = 1; } else { map[i][j] = 0; } } } } void print_map() { int i, j; for (i = 0; i < MAP_SIZE; i++) { for (j = 0; j < MAP_SIZE; j++) { if (map[i][j] == 0) { printf(" "); } else if (map[i][j] == 1) { printf("#"); } else if (map[i][j] == 2) { printf("S"); } else if (map[i][j] == 3) { printf("M"); } else if (map[i][j] == 4) { printf("T"); } else if (map[i][j] == 5) { printf("E"); } else if (map[i][j] == 6) { printf("B"); } } printf("\n"); } printf("Level: %d, HP: %d, Attack: %d\n", level, hp, attack); } int get_random(int min, int max) { srand(time(NULL)); return rand() % (max - min + 1) + min; } void generate_monster() { int i, j; do { i = get_random(1, MAP_SIZE - 2); j = get_random(1, MAP_SIZE - 2); } while (map[i][j] != 0); map[i][j] = 3; } void generate_treasure() { int i, j; do { i = get_random(1, MAP_SIZE - 2); j = get_random(1, MAP_SIZE - 2); } while (map[i][j] != 0); map[i][j] = 4; } void generate_exit() { int i, j; do { i = get_random(1, MAP_SIZE - 2); j = get_random(1, MAP_SIZE - 2); } while (map[i][j] != 0); map[i][j] = 5; } void generate_boss() { int i, j; do { i = get_random(1, MAP_SIZE - 2); j = get_random(1, MAP_SIZE - 2); } while (map[i][j] != 0); map[i][j] = 6; } void move(int x, int y) { if (map[x][y] == 1) { printf("You hit a wall!\n"); } else if (map[x][y] == 2) { printf("You find a sword, attack + 5!\n"); attack += 5; map[x][y] = 0; } else if (map[x][y] == 3) { int monster_hp = get_random(50, 100); printf("You encounter a monster, HP: %d!\n", monster_hp); while (hp > 0 && monster_hp > 0) { printf("Your HP: %d, Monster HP: %d\n", hp, monster_hp); monster_hp -= attack; if (monster_hp <= 0) { printf("You kill the monster!\n"); map[x][y] = 0; break; } hp -= get_random(10, 20); if (hp <= 0) { printf("You are dead!\n"); exit(0); } } } else if (map[x][y] == 4) { printf("You find a treasure, HP + 20!\n"); hp += 20; map[x][y] = 0; } else if (map[x][y] == 5) { printf("You find the exit, level up!\n"); level++; hp = 100; attack += 5; if (level == LEVEL_NUM) { printf("Congratulations, you win!\n"); exit(0); } init_map(); generate_monster(); generate_treasure(); generate_exit(); generate_boss(); } else if (map[x][y] == 6) { int boss_hp = 200; printf("You encounter the boss, HP: %d!\n", boss_hp); while (hp > 0 && boss_hp > 0) { printf("Your HP: %d, Boss HP: %d\n", hp, boss_hp); boss_hp -= attack; if (boss_hp <= 0) { printf("You kill the boss, level up!\n"); level++; hp = 100; attack += 5; if (level == LEVEL_NUM) { printf("Congratulations, you win!\n"); exit(0); } init_map(); generate_monster(); generate_treasure(); generate_exit(); generate_boss(); break; } hp -= get_random(20, 30); if (hp <= 0) { printf("You are dead!\n"); exit(0); } } } else { map[x][y] = 2; } } int main() { init_map(); generate_monster(); generate_treasure(); generate_exit(); generate_boss(); while (1) { print_map(); printf("Please input the direction you want to go (w/a/s/d): "); char direction; scanf("%c", &direction); getchar(); int x = 0, y = 0; switch (direction) { case 'w': x = -1, y = 0; break; case 'a': x = 0, y = -1; break; case 's': x = 1, y = 0; break; case 'd': x = 0, y = 1; break; default: printf("Invalid input!\n"); continue; } move(x + 1, y + 1); } return 0; } ``` 以上代码实现了一个简单的魔塔游戏,包括地图初始化、怪物、宝藏、出口和Boss的生成,以及人物的移动和战斗等基本功能。您可以根据自己的需求进行修改和完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值