318 lines
9.9 KiB
Markdown
318 lines
9.9 KiB
Markdown
# 11.设置条件
|
||
|
||
到目前为止,所有对象的属性都是数据:文本、数字。但属性也可以是代码。
|
||
|
||
在上一章中,我们通过关闭洞穴入口(进入洞穴的通道)来限制玩家的行动自由。这已经让游戏变得更有挑战性了,但这并没有带来多少谜题,除非我们为玩家提供一个微小的可能性来打开通道。真正的挑战应该是让玩家找到通道打开的条件。
|
||
|
||
让我们举一个简单的例子。为了越过守卫进入山洞,玩家必须杀死或贿赂守卫(或两者兼而有之,这很有价值)。换句话说:
|
||
|
||
- 当警卫死亡时(HP=0),入口开放
|
||
- 当警卫拿着银币(贿赂警卫)时,入口开放
|
||
- 两者都不是,入口关闭
|
||
|
||
打开一个封闭的通道(在这里是进入洞穴)涉及到改变一些属性值:
|
||
|
||
- 目的地从 NULL(空地点)变为洞穴
|
||
- <strong>textGo</strong>从 "警卫阻止你...... "改为 "你走进山洞"
|
||
- 在一些特殊情况下,描述和细节不需要改变。但对于一个门洞或栅栏,其中之一(或两者)通常会包含一些从 "开放 "到 "关闭 "的文字。
|
||
|
||
有许多方法来实现这一目标。在这里,我们将讨论一种简单、可维护和通用的方法。
|
||
|
||
首先,我们定义了两个独立的通道:一个代表开放通道,另一个代表封闭通道。除了上面列出的那些,这两条通道在每一个属性上都是相同的。(在你下面看到的生成的地图中,注意有两个箭头通向洞穴;一个是实线,一个是虚线)。
|
||
|
||
接下来,我们引入一个名为条件的新属性,它决定了某个对象是否存在。这两个通道将被赋予互斥的条件,因此在任何时候都只能有一个存在。
|
||
|
||
每个条件将被实现为一个布尔函数:<strong>TRUE</strong>意味着该对象存在,<strong>FALSE</strong>意味着它不存在。
|
||
|
||
```c
|
||
bool intoCaveIsOpen(void)
|
||
{
|
||
return guard->health == 0 || silver->location == guard;
|
||
}
|
||
|
||
bool intoCaveIsClosed(void)
|
||
{
|
||
return guard->health > 0 && silver->location != guard;
|
||
}
|
||
```
|
||
|
||
思考题:你能仿照上面例子自己写一些条件函数吗?
|
||
|
||
新的属性条件是一个指向这样一个函数的指针。
|
||
|
||
```c
|
||
bool (*condition)(void);
|
||
```
|
||
|
||
接下来,我们可以立即开始为 object.txt 中的新属性分配函数。
|
||
|
||
# object.txt
|
||
|
||
```
|
||
- intoCave
|
||
condition intoCaveIsOpen
|
||
description "a cave entrance to the east"
|
||
tags "east", "entrance"
|
||
location field
|
||
destination cave
|
||
detail "The entrance is just a narrow opening in a small outcrop.\n"
|
||
textGo "You walk into the cave.\n"
|
||
|
||
- intoCaveBlocked
|
||
condition intoCaveIsClosed
|
||
description "a cave entrance to the east"
|
||
tags "east", "entrance"
|
||
location field
|
||
prospect cave
|
||
detail "The entrance is just a narrow opening in a small outcrop.\n"
|
||
textGo "The guard stops you from walking into the cave.\n"
|
||
```
|
||
|
||
思考题:尝试自己实现上面的伪代码
|
||
|
||
这两个 "条件 "函数是如此具体,每一个条件函数都只用这一次。现在,我们可以在我们需要的地方定义这些函数。许多编程语言都支持匿名函数,像这样:
|
||
|
||
```
|
||
- intoCave
|
||
condition { return guard->health == 0 || silver->location == guard; }
|
||
...
|
||
|
||
- intoCaveBlocked
|
||
condition { return guard->health > 0 && silver->location != guard; }
|
||
...
|
||
```
|
||
|
||
所以现在我们可以把额外的段落和条件添加到 object.txt 中,就像前面解释的那样。
|
||
|
||
# object.txt
|
||
|
||
```
|
||
#include <stdbool.h>
|
||
#include <stdio.h>
|
||
#include "object.h"
|
||
|
||
typedef struct object {
|
||
bool (*condition)(void);
|
||
const char *description;
|
||
const char **tags;
|
||
struct object *location;
|
||
struct object *destination;
|
||
struct object *prospect;
|
||
const char *details;
|
||
const char *contents;
|
||
const char *textGo;
|
||
int weight;
|
||
int capacity;
|
||
int health;
|
||
} OBJECT;
|
||
|
||
extern OBJECT objs[];
|
||
|
||
- field
|
||
description "an open field"
|
||
tags "field"
|
||
details "The field is a nice and quiet place under a clear blue sky."
|
||
capacity 9999
|
||
|
||
- cave
|
||
description "a little cave"
|
||
tags "cave"
|
||
details "The cave is just a cold, damp, rocky chamber."
|
||
capacity 9999
|
||
|
||
- silver
|
||
description "a silver coin"
|
||
tags "silver", "coin", "silver coin"
|
||
location field
|
||
details "The coin has an eagle on the obverse."
|
||
weight 1
|
||
|
||
- gold
|
||
description "a gold coin"
|
||
tags "gold", "coin", "gold coin"
|
||
location cave
|
||
details "The shiny coin seems to be a rare and priceless artefact."
|
||
weight 1
|
||
|
||
- guard
|
||
description "a burly guard"
|
||
tags "guard", "burly guard"
|
||
location field
|
||
details "The guard is a really big fellow."
|
||
contents "He has"
|
||
health 100
|
||
capacity 20
|
||
|
||
- player
|
||
description "yourself"
|
||
tags "yourself"
|
||
location field
|
||
details "You would need a mirror to look at yourself."
|
||
contents "You have"
|
||
health 100
|
||
capacity 20
|
||
|
||
- intoCave
|
||
condition { return guard->health == 0 || silver->location == guard; }
|
||
description "a cave entrance to the east"
|
||
tags "east", "entrance"
|
||
location field
|
||
destination cave
|
||
details "The entrance is just a narrow opening in a small outcrop."
|
||
textGo "You walk into the cave."
|
||
|
||
- intoCaveBlocked
|
||
condition { return guard->health > 0 && silver->location != guard; }
|
||
description "a cave entrance to the east"
|
||
tags "east", "entrance"
|
||
location field
|
||
prospect cave
|
||
details "The entrance is just a narrow opening in a small outcrop."
|
||
textGo "The guard stops you from walking into the cave."
|
||
|
||
- exitCave
|
||
description "an exit to the west"
|
||
tags "west", "exit"
|
||
location cave
|
||
destination field
|
||
details "Sunlight pours in through an opening in the cave's wall."
|
||
textGo "You walk out of the cave."
|
||
|
||
- wallField
|
||
description "dense forest all around"
|
||
tags "west", "north", "south", "forest"
|
||
location field
|
||
details "The field is surrounded by trees and undergrowth."
|
||
textGo "Dense forest is blocking the way."
|
||
|
||
- wallCave
|
||
description "solid rock all around"
|
||
tags "east", "north", "south", "rock"
|
||
location cave
|
||
details "Carved in stone is a secret password 'abccb'."
|
||
textGo "Solid rock is blocking the way."
|
||
```
|
||
|
||
思考题:尝试自己实现这些功能,并看看与你之前设计的有何不同
|
||
|
||
为了使这些条件发挥作用,我们需要调整函数 isHolding 和 getDistance。
|
||
|
||
# misc.c
|
||
|
||
```c
|
||
#include <stdbool.h>
|
||
#include <stdio.h>
|
||
#include "object.h"
|
||
#include "misc.h"
|
||
|
||
bool isHolding(OBJECT *container, OBJECT *obj)
|
||
{
|
||
return validObject(obj) && obj->location == container;
|
||
}
|
||
|
||
OBJECT *getPassage(OBJECT *from, OBJECT *to)
|
||
{
|
||
if (from != NULL && to != NULL)
|
||
{
|
||
OBJECT *obj;
|
||
for (obj = objs; obj < endOfObjs; obj++)
|
||
{
|
||
if (isHolding(from, obj) && obj->prospect == to)
|
||
{
|
||
return obj;
|
||
}
|
||
}
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
DISTANCE getDistance(OBJECT *from, OBJECT *to)
|
||
{
|
||
return to == NULL ? distUnknownObject :
|
||
!validObject(to) ? distNotHere :
|
||
to == from ? distSelf :
|
||
isHolding(from, to) ? distHeld :
|
||
isHolding(to, from) ? distLocation :
|
||
isHolding(from->location, to) ? distHere :
|
||
isHolding(from, to->location) ? distHeldContained :
|
||
isHolding(from->location, to->location) ? distHereContained :
|
||
getPassage(from->location, to) != NULL ? distOverthere :
|
||
distNotHere;
|
||
}
|
||
|
||
OBJECT *actorHere(void)
|
||
{
|
||
OBJECT *obj;
|
||
for (obj = objs; obj < endOfObjs; obj++)
|
||
{
|
||
if (isHolding(player->location, obj) && obj != player &&
|
||
obj->health > 0)
|
||
{
|
||
return obj;
|
||
}
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
int listObjectsAtLocation(OBJECT *location)
|
||
{
|
||
int count = 0;
|
||
OBJECT *obj;
|
||
for (obj = objs; obj < endOfObjs; obj++)
|
||
{
|
||
if (obj != player && isHolding(location, obj))
|
||
{
|
||
if (count++ == 0)
|
||
{
|
||
printf("%s:\n", location->contents);
|
||
}
|
||
printf("%s\n", obj->description);
|
||
}
|
||
}
|
||
return count;
|
||
}
|
||
```
|
||
|
||
思考题:想想我们调整了什么
|
||
|
||
注意:
|
||
|
||
1. 警卫不可能会死,所以可以说我们的条件函数中的<strong>HP</strong>是很无用的。当然,这很容易通过添加一个 kill 命令来解决,见第 20 章。
|
||
2. 这两个条件函数是互补的;它们有资格成为重复的代码。为了消除这一点,我们可能决定让一个函数调用另一个函数(用'!'操作符来否定结果)。一个匿名函数没有(稳定的)名字,但我们可以用它的对象来指代它。我们可以用 intoCaveBlocked 的条件函数代替。
|
||
3. 为了简单起见,条件函数没有参数。实际上,传递一个参数 OBJECT *obj 可能更好;这使得编写更多的通用条件函数成为可能,可以在多个对象中重复使用。
|
||
4. 在理论上,任何对象都可以成为 "条件"。在下一章,你可以看到一个类似的技术被应用于此。
|
||
|
||
思考题:想一想上面第二点要怎么用 C 来实现?
|
||
|
||
输出样例
|
||
|
||
Welcome to Little Cave Adventure.
|
||
You are in an open field.
|
||
You see:
|
||
a silver coin
|
||
a burly guard
|
||
a cave entrance to the east
|
||
dense forest all around
|
||
|
||
--> go entrance
|
||
The guard stops you from walking into the cave.
|
||
|
||
--> get coin
|
||
You pick up a silver coin.
|
||
|
||
--> give coin
|
||
You give a silver coin to a burly guard.
|
||
|
||
--> go entrance
|
||
You walk into the cave.
|
||
|
||
You are in a little cave.
|
||
You see:
|
||
a gold coin
|
||
an exit to the west
|
||
solid rock all around
|
||
|
||
--> quit
|
||
|
||
Bye!
|