以下是依据 coding 经验。
举例的话,比方 wear 在大部份 mud 都是玩家指令,而不是
特别写在防具上头、或是防具样本档里头的如下形式:
void init()
{
add_action("wear_it","wear");
}
===========分隔线===========
例如说,蛮多的 mud,wiz 在撰写一座城镇的某防具店时,他
可能是这样写的:
inherit SHOP; <= 商店房间样本
void create()
{
::create();
seteuid(getuid(this_object()));
set("short","玛卡村─防具店");
.
.
set("obj_num",([
MAKA_EQ+"plate":10,
MAKA_EQ+"shield":10,
.
.
]));
call_out("load_obj",2);
}
以前这样子写没什么不好,但最近碰到了问题。
我 coding 的 mud,房间档是借由程式生成的,是说如果我撰写
特别的机制,比方它要生成 050.c 这个防具店房间档时,让它
读取如上的内容去做写入,而其它的 001.c~099.c(略过050.c)
的房间档,则读取原本就写好的样本档,那也是可以的。
但通常遇到的情况是,当我某一天想更改地图档,以致于 050.c
不再是防具店,而可能变成 049.c 才是防具店时,我就得去动不
只一个档案。
那于是兴起的一种想法是,有没有更好的做法?
答案是有的,那就是将防具店会用到的指令,全部写成玩家指令
例如说,buy 这个指令,实际上玩家在任何的地方都可以下,但
是唯有在特殊的地方,buy 才有作用:
ppl=this_player();
room=environment(ppl);
// 当玩家所在的房间并不是商店时
// 这里对商店的唯一定义就是有 obj_num 这个资料
if(!room->query("obj_num"))
return notify_fail("什么?\n");
同样的,list、sell、identify、.. 这些我认为,也应该写成玩
家指令。
这时会产生的问题就是,比方 list,嗯,不是只有商店才会用到
list 啊,任务店也会啊,那任务店没有 obj_num,读取的比方是
像 quest_list 这样的参数,那怎么办?
ppl=this_player();
room=environment(ppl);
if(room->query("obj_num"))
{
执行相对应的商店 list 程式段;
return 1;
}
else if(room->query("quest_list"))
{
执行相对应的任务店 list 程式段;
}
.
.
// 剩下的情况就是一般房间
return notify_fail("什么?\n");
这时形成的概念就是,以往由各种继承档来形成的各种形形
色色房间的情况,改成从现在开始,所有房间一视同仁,全
部房间的程式档内容近乎相同,改成以底下的东西来区别这
些房间的差异:
一、描述。例如[防具店],[这是村子里唯一的防具店,..]
以打怪的区域来说,大部份房间的名称是一样的,例如
[洞穴内的道路],只有极少数例外。
二、被额外设定的参数。例如 obj_num、quest_list、..
所谓被额外设定的参数,指的就是当以程式自动产生某个区
域的所有房间之后,该区域会有控制房,假设是 control.c
,它负责“设计”哪些房间应该 set 哪些设定:
// 房间用途与房号对应定义区
#define ARMOR_NUM 050
// 第一区: 设定房间名字
switch(room_num)
{
case 001: room->set("short","村子入口"); break;
case ARMOR_NUM: room->set("short","防具店"); break;
.
.
}
// 第二区: 设定房间叙述
switch(room_num)
{
case ARMOR_NUM: room->set("long",@LONG
这里是村子里唯一的一间防具店,你看到老板通常都悠闲地坐在
柜台的后方打盹儿,只有当客人上门的时候,才会慵懒地起身打
招呼。
LONG
);
}
break;
}
// 第三区: 设定额外资料
switch(room_num)
{
case ARMOR_NUM: room->set("obj_num",([
MAKA_EQ+"plate":10,
MAKA_EQ+"shield":10,
]));
break;
}
这样当玩家进到 050 这间房间时,它就会看到不同于其它房间
的名称、叙述,而在 050 被设定了 obj_num 的前提下,它在这
个房间就可以使用 list、buy、sell .. 等玩家指令。
好处有以下几个
一、对区域的 coding 不再是一个房间一个房间去做依用途不同
的程式档撰写。不用写房间了,房间靠程式读取像是地图路
线资料后来产生。(连出口都自动设好了)
二、对 control.c 区域档的诸多设定,就变得有点像 control.c
是这个区域的各房间细部设计稿,在里面对各种不同用途的
房间做各种不同的 room->set() 动作。
三、这时,搭配已写好的各种玩家指令,各个房间就自然依设定
赋予了用途。将来假设改了地图、新增或减少了房间,只需
要改 control.c,例如 050 变成了 049
#define ARMOR_NUM 049
再启动一次房间生成,这样 049 就会被 control.c 设定防
具店该有的资料。
最大的好处,就是大多数人通常懂设定,但不见得懂程式。而这
个 coding 环境可让只懂设定的人也能参与 control.c 的内容设
计。理论上这个人懂设计,就能生成区域。
简单的说就是
之前
我直接写防具店的程式档,里面也已写好该有的程式码,包
含 set("short", set("long", set("obj_num",..
当玩家进来防具店、或是这个防具店被加载时,它就是一间
防具店。
以后
所有的房间都透过程式读取地图档及房间样本稿来生成
之后我透过 control.c 做像是设计稿的 design 工作
然后唯有玩家进入像 050 这个房间,此时它呼叫control.c
control.c 才依设计稿对 050 做“它是一间防具店”的诸多
设定(set)。或是这个房间被加载时,才做设定。
这样子的好处就是,每一座区域的特殊房间资料,都在 control.c
里头,一目了然,区域作者或未来接手的人,想了解这个区域的资
料时,只要看该区域的 control.c 就知道了。
(这样理论上 control.c 也能生成 QC 用的文件)
缺点则是,对于已经存在许久的 mud,就不好这样子做了,所以比
较推荐,新的 mud 可以采取这样的做法。以房间来说,尽可能让
每一个区域房间的程式档内容是单纯的,然后透过像是玩家指令的
东西,来区别玩家在不同用途的房间时可以执行的动作为何。
第二个缺点则可能是玩家可用指令会变得贼多,但我是认为,符合
直觉的指令再多都没关系,比方 buy sell list 这些。现在电脑
效能很好了,只要 driver 支援,就不用太顾虑 rehash 问题。
以上一点分享。GPT 早五年出来,我或许就会架一个这样的 mud。