8. 移动方向
传统的文本冒险使用指南针方向进行导航。
例如,我们在第 6 章中绘制的地图上,玩家可能想向东移动,从田野移动到洞穴。我们可以通过给连接 Cave 的通道标上 “east” 来实现这一点。但是,我们首先需要解决两个问题。
- 我们可能仍然想把这段通道称为 “entrance” 和 “east”。但现在,一个对象只能有一个标签。
- 在更大的地图上,具有更多的位置和道路,标签 “east” 将被定义多次。到目前为止,标签在我们的游戏中是全球独一无二的,没有重复项。
🤔 思考题:你能否想出解决办法?
这些问题同样适用于其他对象,而不仅仅是通道。
在我们的冒险中,我们有一枚银币和一枚金币。一方面,如果不接受在只有一枚硬币存在的地点得到硬币,那将是愚蠢的。
另一方面,在两种硬币都在同一位置存在的情况下,玩家应该可以有两个拾取选择。
这立即将我们带到了解析器的第三个问题:
- 一个标签只能是一个单词;“sliver coin” 这是俩单词,他不接受啊
所有三个问题都将在本章中解决,从问题 #1 开始。它通过为每个对象提供一个标签列表来解决,而不仅仅是一个标签。
object.h
c
typedef struct object {
const char *description;
const char **tags;
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 wallField (objs + 8)
#define wallCave (objs + 9)
#define endOfObjs (objs + 10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
object.c
c
#include <stdio.h>
#include "object.h"
static const char *tags0[] = {"field", NULL};
static const char *tags1[] = {"cave", NULL};
static const char *tags2[] = {"silver", "coin", "silver coin", NULL};
static const char *tags3[] = {"gold", "coin", "gold coin", NULL};
static const char *tags4[] = {"guard", "burly guard", NULL};
static const char *tags5[] = {"yourself", NULL};
static const char *tags6[] = {"east", "entrance", NULL};
static const char *tags7[] = {"west", "exit", NULL};
static const char *tags8[] = {"west", "north", "south", "forest", NULL};
static const char *tags9[] = {"east", "north", "south", "rock", NULL};
//我们不固定标签长度,在结束的时候用 NULL 来标记
OBJECT objs[] = {
{"an open field" , tags0, NULL , NULL },
{"a little cave" , tags1, NULL , NULL },
{"a silver coin" , tags2, field, NULL },
{"a gold coin" , tags3, cave , NULL },
{"a burly guard" , tags4, field, NULL },
{"yourself" , tags5, field, NULL },
{"a cave entrance to the east", tags6, field, cave },
{"an exit to the west" , tags7, cave , field },
{"dense forest all around" , tags8, field, NULL },
{"solid rock all around" , tags9, cave , NULL }
//我们用 NULL 来阻绝进入一个你不知道的地方
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
当然,要让这个改动生效,我们还需要调整 noun.c 中的 objectHasTag 函数。
同时,我们将让函数 getVisible 和 getPossession 告知玩家他必须更具体的选择你到底是银币还是金币,而不是随机选择任何一个对象。
noun.h
c
extern OBJECT *getVisible(const char *intention, const char *noun);
extern OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun);
1
2
2
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)
{
if (noun != NULL && *noun != '\0')
{
const char **tag;
for (tag = obj->tags; *tag != NULL; tag++)
{
if (strcmp(*tag, noun) == 0) return true;
}//扫描对象的 tag 列表
}
return false;
}
static OBJECT ambiguousNoun;//我们需要这玩意的地址帮助我们把它当成一个返回值
static OBJECT *getObject(const char *noun, OBJECT *from, DISTANCE maxDistance)
{
OBJECT *obj, *res = NULL;
for (obj = objs; obj < endOfObjs; obj++)
{
if (objectHasTag(obj, noun) && getDistance(from, obj) <= maxDistance)
{
res = res == NULL ? obj : &ambiguousNoun;//标签不明确的解决方案哦
}
}
return res;
}
OBJECT *getVisible(const char *intention, const char *noun)
{
OBJECT *obj = getObject(noun, player, distOverthere);
if (obj == NULL)
{
if (getObject(noun, player, distNotHere) == NULL)
{
printf("I don't understand %s.\n", intention);
}
else
{
printf("You don't see any %s here.\n", noun);
}
}
else if (obj == &ambiguousNoun)//模糊匹配
{
printf("Please be specific about which %s you mean.\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, from, distHeldContained)) == NULL)
{
if (getObject(noun, player, distNotHere) == NULL)
{
printf("I don't understand what you want to %s.\n", verb);
}
else 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);
}
}
else if (obj == &ambiguousNoun)//模糊匹配
{
printf("Please be specific about which %s you want to %s.\n",
noun, verb);
obj = NULL;
}
else if (obj == from)
{
printf("You should not be doing that to %s.\n", obj->description);
obj = NULL;
}
return obj;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
问题 #3 可以通过从函数 parseAndExecute 中删除一个 空格字符来解决(下面的第 10 行)。这个解决方案远非完美('silver' 和 'coin' 之间的双空格是打咩的),但直到我们在第 13 章中让自己成为一个更好的解析器之前。
parsexec.c
c
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "location.h"
#include "inventory.h"
bool parseAndExecute(char *input)
{
char *verb = strtok(input, " \n");
char *noun = strtok(NULL, "\n");
if (verb != NULL)
{
if (strcmp(verb, "quit") == 0)
{
return false;
}
else if (strcmp(verb, "look") == 0)
{
executeLook(noun);
}
else if (strcmp(verb, "go") == 0)
{
executeGo(noun);
}
else if (strcmp(verb, "get") == 0)
{
executeGet(noun);
}
else if (strcmp(verb, "drop") == 0)
{
executeDrop(noun);
}
else if (strcmp(verb, "give") == 0)
{
executeGive(noun);
}
else if (strcmp(verb, "ask") == 0)
{
executeAsk(noun);
}
else if (strcmp(verb, "inventory") == 0)
{
executeInventory();
}
else
{
printf("I don't know how to '%s'.\n", verb);
}
}
return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
模块 main.c、inventory.c、location.c、move.c 和 misc.c 保持不变
现在对象数组 ( object.c ) 开始在多个维度上增长(特别是在引入多个标签的情况下),我们需要一种使其更易于维护的方法。
🤔 猜猜看该怎么办?