Files
fzu-product/3.编程思维体系构建/3.4.6.6.绘制地图.md
2023-04-14 15:41:04 +08:00

337 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 6.绘制地图
作为一个 RPG 游戏怎么能没有地图呢,<em>是时候绘制地图了!</em>
绘制地图的最佳工具始终是:一支铅笔和一张纸。基本地图由<strong>位置</strong>(矩形)组成,由道路(箭头)连接。我们已经在第 3 章中创建了位置,现在我们将开始添加道路。
在虚拟世界中,“道路”可能是连接两个位置的任何东西:一条路,一扇门,沙漠中。基本上,一段经文具有以下属性:
- 起点(位置)。
- 目标(位置)。
- 叙述性描述,例如“森林小径”。
- 在 <em>go</em> 命令中往哪里走的描述性标记
考虑到这些属性,第 4 章中定义的结构对象就非常适合存储道路了。事实上,一个道路与一个项目或 NPC 并没有太大的不同它作为“可见出口”存在于某个位置该位置是起点。它只是与某些命令的行为不同特别是命令“go”应用于道路<em>go</em>将改变玩家的位置。
```c
struct object {
const char *description;
const char *tag;
struct object *location;
struct object *destination;
};
```
注意:
- 显然,<em>目的地</em>在大多数其他对象物品NPC中都没有使用
- 通道总是朝一个方向运行;要双向连接两个位置我们总是必须创建两个单独的通道。乍一看这似乎很笨拙但它确实给了我们很大的灵活性来完善命令“go”的行为
- 在大地图上,你可能会发现手动创建所有通道很乏味。所以,我强烈建议你使用自定义工具<em>生成</em>地图中重复性更强的部分。这里不会介绍这一点,但您可能会在第 9 章中找到一些灵感,我们将在其中讨论自动胜场。
思考题:为什么创建两个通道可以使我们的程序更加灵活?
接下来我们将展开对象数组
# object.h
```c
typedef struct object {
const char *description;
const char *tag;
struct object *location;
struct object *destination;
} OBJECT;
extern OBJECT objs[];
#define field (objs + 0) //是不是觉得这个很熟悉
#define cave (objs + 1)
#define silver (objs + 2)
#define gold (objs + 3)
#define guard (objs + 4)
#define player (objs + 5)
#define intoCave (objs + 6)
#define exitCave (objs + 7)
#define endOfObjs (objs + 8)
```
# object.c
```c
#include <stdio.h>
#include "object.h"
OBJECT objs[] = {
{"an open field" , "field" , NULL , NULL },
{"a little cave" , "cave" , NULL , NULL },
{"a silver coin" , "silver" , field, NULL },
{"a gold coin" , "gold" , cave , NULL },
{"a burly guard" , "guard" , field, NULL },
{"yourself" , "yourself", field, NULL },
{"a cave entrance", "entrance", field, cave },
{"an exit" , "exit" , cave , field }
};
```
我们将在 <em>misc.c</em> 中添加一个小的帮助函数,以确定两个给定位置之间是否存在通道。
# misc.h
```c
extern OBJECT *getPassage(OBJECT *from, OBJECT *to);
extern OBJECT *actorHere(void);
extern int listObjectsAtLocation(OBJECT *location);
```
# misc.c
```c
#include <stdio.h>
#include "object.h"
OBJECT *getPassage(OBJECT *from, OBJECT *to)
{
if (from != NULL && to != NULL)
{
OBJECT *obj;
for (obj = objs; obj < endOfObjs; obj++) //寻找物品之间是否具有通道
{
if (obj->location == from && obj->destination == to)
{
return obj; //找到了
}
}
}
return NULL;//找不到
}
OBJECT *actorHere(void)
{
OBJECT *obj;
for (obj = objs; obj < endOfObjs; obj++)
{
if (obj->location == player->location && obj == guard)
{
return obj;
}
}
return NULL;
}
int listObjectsAtLocation(OBJECT *location)
{
int count = 0;
OBJECT *obj;
for (obj = objs; obj < endOfObjs; obj++)
{
if (obj != player && obj->location == location)
{
if (count++ == 0)
{
printf("You see:\n");
}
printf("%s\n", obj->description);
}
}
return count;
}
```
我们将在命令“go”的实现中使用新功能<em>getPassage</em>来确定是否存在可以将玩家带到所需位置的通道。
# location.h
```c
extern void executeLook(const char *noun);
extern void executeGo(const char *noun);
```
# location.c
```c
#include <stdio.h>
#include <string.h>
#include "object.h"
#include "misc.h"
#include "noun.h"
void executeLook(const char *noun)
{
if (noun != NULL && strcmp(noun, "around") == 0)
{
printf("You are in %s.\n", player->location->description);
listObjectsAtLocation(player->location);
}
else
{
printf("I don't understand what you want to see.\n");
}
}
void executeGo(const char *noun)
{
OBJECT *obj = getVisible("where you want to go", noun);
if (obj == NULL)
{
// already handled by getVisible
}
else if (getPassage(player->location, obj) != NULL)
//go只会在有地方的时候才会运行起来
{
printf("OK.\n");
player->location = obj;
executeLook("around");
}
else if (obj->location != player->location)
{
printf("You don't see any %s here.\n", noun);
}
else if (obj->destination != NULL)
{
printf("OK.\n");
player->location = obj->destination;
executeLook("around");
}
else
{
printf("You can't get much closer than this.\n");
}
}
```
我们还将使用新功能<em>getPassage</em>来确定从玩家站立的位置是否可以看到某个位置。未通过通道连接到当前位置的位置不被视为可见。
# noun.h
```c
extern OBJECT *getVisible(const char *intention, const char *noun);
extern OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun);
```
# noun.c
```c
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "object.h"
#include "misc.h"
static bool objectHasTag(OBJECT *obj, const char *noun)
{
return noun != NULL && *noun != '\0' && strcmp(noun, obj->tag) == 0;
}
static OBJECT *getObject(const char *noun)
{
OBJECT *obj, *res = NULL;
for (obj = objs; obj < endOfObjs; obj++)
{
if (objectHasTag(obj, noun))
{
res = obj;
}
}
return res;
}
OBJECT *getVisible(const char *intention, const char *noun)
{
OBJECT *obj = getObject(noun);
if (obj == NULL)
{
printf("I don't understand %s.\n", intention);
}
else if (!(obj == player ||
obj == player->location ||
obj->location == player ||
obj->location == player->location ||
getPassage(player->location, obj) != NULL || //检查两个位置是否相邻
(obj->location != NULL &&
(obj->location->location == player ||
obj->location->location == player->location))))
{
printf("You don't see any %s here.\n", noun);
obj = NULL;
}
return obj;
}
OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun)
{
OBJECT *obj = NULL;
if (from == NULL)
{
printf("I don't understand who you want to %s.\n", verb);
}
else if ((obj = getObject(noun)) == NULL)
{
printf("I don't understand what you want to %s.\n", verb);
}
else if (obj == from)
{
printf("You should not be doing that to %s.\n", obj->description);
obj = NULL;
}
else if (obj->location != from)
{
if (from == player)
{
printf("You are not holding any %s.\n", noun);
}
else
{
printf("There appears to be no %s you can get from %s.\n",
noun, from->description);
}
obj = NULL;
}
return obj;
}
```
显然,此示例中的地图是微不足道的:只有两个位置,并且它们在两个方向上都连接在一起。第 12 章将增加第三个地点。
思考题:你能否绘制一张更精细的地图,并将其变成对象列表(位置和通道)
```
注:不用当成任务,自行实验即可
```
输出样例
Welcome to Little Cave Adventure.
You are in an open field.
You see:
a silver coin
a burly guard
a cave entrance
--> go entrance
OK.
You are in a little cave.
You see:
a gold coin
an exit
--> go exit
OK.
You are in an open field.
You see:
a silver coin
a burly guard
a cave entrance
--> go cave
OK.
You are in a little cave.
You see:
a gold coin
an exit
--> quit
Bye!