
在这一章里面, 你将会认识一种更复杂的数据类型━━物件. 一个物件变量指向
一个已经载入 driver 记忆体的真正物件. 宣告物件变量的方法跟宣告其他数据
object ob
不过它不同的地方在於你不能在它身上用 +、- 、+=、-=、* 、/ (把一支怪物
除以另一支怪物到底有啥意义 ? ). 而且, 像是 say() 和 write() 外部函数
只要字串或整数, 你就不能 write() 或 say() 它们 (再次声明, 说一支怪物
是啥意思 ? ). 但是你可以将它们用於其他 LPC 重要的外部函数上.

8.3 外部函数: this_object()

这个外部函数传回一个物件, 是正在执行 this_object() 的物件. 换句话说, 
在一个文件里, this_object() 就是你的文件物件复制出去的拷贝或是继承这个
文件的其他文件. 当你正在撰写一个会被别的文件继承的文件, this_object() 
就很有用. 假设你正在写你自己的 living.c , user.c 和 monster.c 会继承
它, 但是 living.c 不可能会独自使用, 它只用来被这两个物件继承. 你想要把
设定玩家等级的 set_level() 函数记录下来, (但是你不想记怪物的).

void set_level(int x) {
if(this_object()->is_player()) log_file(levels, foo\n)
}

既然 living.c 或 living.c 继承的文件都没有定义 is_player(), 我们就假设
if(is_player()) 会导致一个错误, 因为 driver 在你的文件里、你继承的
文件中都找不到 is_player() 函数. 因为你的文件是被别的文件继承之故, 
this_object() 让你能使用最後成品中可能拥有 (或没有) 的函数而不会出现错
误.

8.4 呼叫其他物件中的函数

这当然是向你介绍物件数据类型最重要的特色. 它让我们能使用其他物件中的函
式. 前面的范例里, 你已经能找出一个玩家的等级、减少他们身上的钱、他们有
多少可承受伤害点数.

物件->函数(参数)
call_other(物件, 函数, 参数)

this_player()->add_money(silver, -5)
call_other(this_player(), add_money, silver, -5)

某些情形下 (很概略的说法) , 游戏只是由玩家命令触发的一连串函数呼叫. 当
一个玩家开始一串函数呼叫时, 这个玩家就是 this_player() 外部函数所传回
的物件. 所以, 因为 this_player() 可以由触发事件的人决定, 你要小心你用
this_player() 呼叫函数的地方在哪里. 你通常会把它摆在最後一个重要的区域
函数━━ init() 里 (我们已经提过 create() 和 reset() ).

8.5 区域函数: init()

任何时候, 一个活著的东西碰到一个物件 (进入一个新的房间, 或其他物件进入
同一个房间) , 就会呼叫此物件新遇到所有物件里面的 init() 函数. 在此, 你
可以加上一些玩家可以使用的命令. 以下是一朵花的 init() 函数范例.

void init() {
init()
add_action(smell_flower, smell)
}

上面呼叫 smell_flower() 函数. 所以你应该有个 smell_flower() 函数长得像

1 int smell_flower(string str)
/* 玩家动作的函数是整数型态 */
2
3 int smell_flower(string str) {
4 if(str != flower) return 0
/* 玩家闻的不是这朵花 */
5 write(你闻了这朵花.\n)
6 say((string)this_player()->query_cap_name()+闻了闻花.\n)
7 this_player()->add_hp(random(5))
8 return 1
9 }

第一行, 我们宣告函数.
第三行, 开始 smell_flower(). str 是跟在玩家命令之後的任何东西 (不包括
第一个空白字元).
第四行, 检查玩家输入的是否为 smell flower. 如果玩家输入的是
"smell cheese", 则 str 就是 "cheese". 如果闻的不是花, 就传回 0, 
让 driver 知道不该呼叫这个函数. 如果玩家身上有块乳酪, 乳酪也有个
smell 指令的话, driver 之後会呼叫乳酪的函数. driver 会持续呼叫同
样是 smell 的命令, 直到有一个传回 1 为止. 如果它们都传回 0, 则玩
家就看到「什么 ?」 
第五行, 呼叫 write() 外部函数. write() 把传入给它的字串印出来给
this_player() . 所以, 只要输入 smell flower 的玩家都会看到「你闻
了这朵花.」
第六行, 呼叫 say() 外部函数. say() 印出闻花动作的字串, 我们需要呼叫
this_player() 的 query_cap_name() 函数. 这样子碰上隐形的玩家会印出
「某个人」 (或像是隐形的东西), 而且会把第一个字元转为大写
(capitalize).
第七行, 我们呼叫 this_player() 物件中的 add_hp() 函数, 因为我们想在闻
了花之後对玩家作一点治疗 (注: 别把这些程式码写在你的 mud 里, 管理
mud 平衡的人会毙了你).
第八行, 我们把游戏的控制交回给 driver, 传回 1 让 driver 知道它呼叫的
函数正确.

8.6 在你的房间加上物件

现在, 使用物件数据类型, 你可以把怪物加进房间里面:

void create() {
create()
set_property(light, 3)
set(short, Krasna 广场)
set(long, 欢迎来到 Praxis 镇的中央广场.\n)
set_exits( ({ d/standard/hall }), ({ east }) )
}

void reset() {
object ob

reset()
if(present(guard)) return
/* 如果已经有一位警卫, */
/* 就别再增加一位 */
ob->set_name(guard)
ob->set(id, ({ guard, town guard }) )
ob->set(short, 镇警卫)
ob->set(long, 它看守著 Praxis.\n)
ob->set_gender(male)
ob->set_race(human)
ob->set_level(10)
ob->set_alignment(200)
ob->set_humanoid()
ob->set_hp(150)
ob->set_wielding_limbs( ({ right hand, left hand }) )
ob->move(this_object())
}

现在, 大多数的 mud 在此都大不相同. 前面提过, 有的 mud 把这些东西写在
一个独立设定的怪物物件里. 原始模式的 mud 最後要呼叫怪物物件中的 move()
来把它搬进房间 (this_object() ) 里. 在精简模式的 mud 里, 你呼叫需要两
个参数的 move_object() 外部函数, 这两个参数是: 要搬动的物件和要放东西
进去的物件.

