***********************************************************************************

                         wiz 编程杂谈 (1)

关于notifal_fail

usage: notofy_fail(string | function str);
请注意：notify_fail() always returns 0.

nofify_fail() 的作用是设置本次命令的失败信息。嗯，听起来好像很复杂 :P
其实就是取代“什么？”这句话的东东啦。比如我们打一个命令(包括动作什么的)。
要是此命令的程序return 0 的话，系统就会给个错误信息 “什么？”但假如我们
在命令的程序里面有notify_fail() ，那么假如命令最后返回 0 。那系统就给出
本命令执行的最后一个notify_fail() 所设定的错误信息，否则就是“什么？”。

其中notify 的参数可以是一个str 的function 。就是把这个func 所返回的
string 做错误信息了。

例如：help 命令里的一段：

        if( stringp(file = me->find_command(arg)) ) {
                notify_fail("有这个指令存在，但是并没有详细的说明文件。\n");
//在执行notify_fail 的时候并没显示，只是在最后返回0 值时才显示此信息。
                return file->help(me);
        }

//要是return file->help(me) == 0 的话。
//就会给出错误信息"有这个指令存在，但是并没有详细的说明文件。\n"
//假如没notify_fail 这句，错误信息就是 “什么？”


标  题: room的制作


/doc/build/room
□  如何建造一个房间

    房间是构成这整个世界的要素之一，在此我们提供了一个房间的标准物件来让
所有的房间继承。而如同其他的物件一般，你需要写一个 create() 来设定房间中
的叙述、出口、物品、生物等等。这里，我喜欢说你用 create() 这个函式来赋予
这个房间的属性。一般来说，要建造一个简单的房间，你只要赋予它基本的属性即
可。当然，我们不认为一个区域中几十个房间没有任何的机关或秘密，是个会吸引
玩家一游的好地方。

    下面，提到了一些建造房间所需要留意的事项，也会配合一些例子来说明。

  一、基本篇

    一个基本的房间，要有 short <短叙述> 、 long <长叙述>、 exits <出口>

□  当你在写一个房间的 long <长叙述>时，其格式为：

        set("long", @LONG

房间的叙述.......

LONG
        );
      
    其中 @LONG 和 LONG  是互相对应的，你可以用任何字接在 @  後面，但是前
後两个字一定要一样，这样系统才能判别，而房间的叙述写完时，一定要换行後再
接第二个 LONG ，且同一行不能再有其他任何的字元，不然系统无法判定叙述是否
该结束了，会造成编译时的错误。

    而为求区域看起来外观上整齐、统一，房间的长叙述中每一行的长度必须一样
，而一行的长度建议为 29 到 32 个中文字，约占萤幕的三分之二。并且一个房间
的叙述最好不要低於三行，区域各个房间的叙述重复性降到越低越好，这样你的区
域看起来才不会太过阳春。当然，有时候为了某些目的，比如一个迷宫，你可能会
相邻的几个房间都用到一样的叙述，那自然不在此限。

□  一个房间的出口则以下列格式赋予：

        set("exits", ([
                "方向"          : "连接到的房间之档名",
                ...........
        ]);

    在这里，为了一个以後区域开放後搬移目录的便利性，建议采用__DIR__ 这个
由系统提供的巨集来写路径，比如说：

                "west"          : __DIR__"path3",
        和
                "west"          : "/u/d/davidoff/goathill/path3",

是完全一样的。但前者显然在以後目录的搬移上方便的多。而在下面会提到设定房
间中的物品或生物时，也建议采用这种方式写作。

□  item_desc 这是用来设定个别景物的描述，当玩家用 look 这个指令时就会作
    用。其格式为：

        set("item_desc", ([
                "景物名称"      : "景物叙述",
                ...........
        ]);

    其中景物叙述可以是字串或是一个 function ，所以你可以利用这个功能加以
变化，当玩家 look 一个景物时，可能看到叙述，也可能发生一些特殊的事件，而
你就可以在被呼叫的函式中写下这些事件。

□  objects 可以让这个房间在每次 reset时载入某些生物或某些物品：

        set("objects", ([
                "物品或生物的档名"      : 数量,
                ...........
        ]);

    如同前面所提到的，建议采用 __DIR__来编写你的路径，而数量则要用整数。
 
□  要为这个房间添上门户时，记得前面必须先 #include <room.h>。而格式为：

        create_door("出口方向", "门的名称", "进入方向", 预设状态);

    比如说，这里明显的出口有 west、east 和 up。 而你要让西边有一个关上的
红木门，你可以这样写：

        create_door("west", "红木门", "east", DOOR_CLOSED);

    当玩家进入这个房间时，他会看到：

        这里明显的出口有 east 和 up。

    而当他 look west 时，会看到：这个红木门是关上的。

 
    其他的一些属性，你可以参考 /doc/build/room_prop 或是读一下标准物件的
room.c。也建议你可以多用 more here来观看一间特殊的 room。

  二、进阶篇

    要让你的区域中富有变化，生动有趣，除了文字叙述的丰富度以外，你更可以
利用 init() 这个函式为你的房间增加一些「机关」或「秘密」。

    这里，先让我们了解一下 init() 的用途为何，和为什麽要用到它。每一个房
间的 create() 只有当 reset时才会被呼叫到，而 init() 则是在 B物件进入到 A
物件时都会呼叫到 A物件的 init() 。看到这，你应该可以看出差别了，我们希望
当一个物件（此处较多是玩家）进到一个房间时，能够经由某个动作启动这个房间
的机关的话，自然是利用 init() 来编写。

    一般的使用方式，是在 init() 中利用 add_action() 来呼叫你写的函式，其
格式为：
        add_action("function type", "action");

    function type 即是被呼叫的函式名    action 是启动的动作

    而你就可以将被 action 启动後要发生的事，都写在被呼叫的函式里面。理论
上来说，利用这个方式我们可以做到任何事，当然，能不能达成就看写程式的功力
了。下面举个简单的例子：

void init()
{
        add_action("do_pick", "pick");
}

int do_pick(string arg)
{
        object me;

        me = this_player();
        if ( !arg || ( arg != "flower" ) ) return 
notify_fail("你要摘什麽？\n");

        else if ( random((int)me->query("kar")) < 7 )
                message_vision("$N将花摘了下来，但一不小心被刺了一下。\n", 
me);

        else
                message_vision("$N摘下一朵美丽的血红色鲜花。\n", me);

        return 1;
}
   
    当玩家利用 pick 这个指令时就会呼叫到 do_pick() 这个 function，而启动了
这个房间的机关。

    这里特别提到一点，一个简单的 room 我们为了使记忆体的使用量降到最低，会
在 create() 最後加上一行 replace_program(ROOM); 。这是因为在房间的标准物件
中有定义了如 init() 等其他的函式，而一个简单的房间根本没有用到，所以我们用
replace_program() 来将原本的被继承的标准物件「重置」（或说取代）掉，但是一
旦房间中用到了 init() 来编写时，就绝对不可以用 replace_program()，因为系统
届时找不到 init() 便会随便呼叫一个记忆体中的位址而随便传进一些乱七八糟的东
西，情况严重时，甚至可以让整个 mud crash。但是，我们自不可因噎废食，该用的
时候还是要用，这些应该是一个好的程式写作人员自己必须留意的，发生状况要自己
负责。

  三、建议

    这里我们提供了一个工具来让巫师们可以方便的编写一个房间，那就是房间编辑
器(Roommaker) ，你可以 clone /obj/roommaker 来使用它。一般的步骤是，先利用
mkroom来造一个空房间，然後利用 goto 这个指令到房间里去，再用 rset short 和
connect 来设定这个房间的短叙述及出口，而像其他的一些属性例如 outdoors 等等
也都可以利用他来做到，接著用 to rset long 来设定这个房间的长叙述，最後再用
saveroom将这个房间存档。要是这只是一个基本的房间，那到这里就大功告成了，要
是□想让这个房间富有变化，那就再用线上编辑器 edit 或将这个房间的档案 ftp
回去继续修改。

Subject: room的属性 (fwd)

/doc/build/room_pro

□ 房间属性

"short"                                                         (string)

    房间的短叙述。

"long"                                                          (string)

    房间的长叙述。

"item_desc"                                                     (mapping)

    房间中个别景物的叙述，格式为：([ <景物名称>:<景物叙述>, .... ])。
    其中<景物叙述>可以是字串或 function type。

"exits"                                                         (mapping)

    房间的出口，包括有门的方向，格式为：([  <出口>:<房间档名>, .... ])。

"objects"                                                       (mapping)

    房间中的物品、生物，格式：([ <物品或生物档名>:<数量>, .... ])。

"outdoors"                                                      (string)

    房间是否为「户外」，户外房间可以看到天色变化与气候影响。字串的意义
    表示房间的气候区，通常和该区域的 domain (即 /d 下的目录名称) 同。

"no_fight"                                                      (int)

    房间是为禁止作战区域。

"no_magic"                                                      (int)

    房间是为禁止施法区域。

**************************************************************************************
***********************************************************************************

                         1。LPC入门(上)


LPC就是我们用来写MUD的语言啦，它的语法和C 基本一样。它独特之处在于有简单的
OOP特性(简单但很有用:PP)，还有一个C里面没有的HASH表的类型：MAPPING
LPC和C还有一个不同是其主函数是CREATE()而不是MAIN()。create()别写错哦:PP

    LPC里面主要的(也是写MUD所足够的)数据类型有int,string,mapping,object,
mixed。下面主要讲一下这些类型了。

    因为整数类型对于写MUD已经足够，所以不用FLOAT了。同样的，因为我们的
汉字是双字节的，所以CHAR类型其实也没用，只需要STRING 类型就可以了。
STRING的定义，这里要提一下：string常量的赋值，假如是常量的话可以只用
“连接”的办法代替string，例如:
        string str = "我" "们" ;那么结果是str == "我们"
同样的str = 
                "我"
                "们";也是一样，空格与换行在LPC编译时是被忽略的，所以我们
写MUD的时侯不仿多些TAB和换行，这样程序容易看些。
当然，除了直接连接之外还可以用 + 连接。在有变量的时侯就要用了。例如：
        string str0 = "我们";
        string str1 = "和"+str0+"大家";
        结果是str1 == "和我们大家"

    mixed是一个比较特别的类型。mixed 类型的变量可以赋任何其他类型的值。
这在未知变量类型的时侯非常有用。不过一般情况下很少会用到mixed。

    LPC里面没有“指针”的概念。在变量名前面加 * 的定义表示数组。如int *a
表示a 是整数数组。一般来说我们定义数组时是未知其大小的。所以定义时不用象
C 那样给定大小。数组和MAPPING有些类似，所以将在下面和MAPPING一起讲它们
的操作。

**************************************************************************************
*********************************************************************************
              
                             LPC入门 （中）


    LPC里面没有“指针”的概念。在变量名前面加 * 的定义表示数组。如int *a
表示a 是整数数组。一般来说我们定义数组时是未知其大小的。所以定义时不用象
C 那样给定大小。数组和MAPPING有些类似，所以将在下面和MAPPING一起讲它们
的操作。

    object, 是OOP概念了，在LPC里面好象有CREATE()的都可以做object 类型变量
了。object 我们称为“对象”，在MUD里就是一件物品，一个房间或任何一个“具
体”的东西，都是OBJECT。“对象”，在OOP中是一些数据与基于这些数据的函数的
集合（好象文诌诌的？ :PP）嗯，object 中的数据一般不能直接操作（至少在LPC
里面不能的 :PP），所以对object的操作只有赋值（对object类型的赋直 ）和执行
object的函数( private函数不能被外部调用 )。
函数调用格式：
eg.     object ob;
        ob->test(1,2);

        基本格式： object类型变量名->函数名(函数参数列);
其中若该object 中未定义该函数名的函数则返回 0 值（呵呵，不会有出错信息的哦，
所以千万别写错名字了）。

    写了这么多终于写到LPC 最有特色的两个类型了，mapping和数组。
mapping和数组在“外观”上有些类似，所以在一起写了。前面提到过：mapping是
散列表，具体如何这里不详述了，只希望大家一定要记住mapping的格式！！( 实际
上这格式只在给变量赋初值时用到 )mapping 格式如下：
eg.     mapping a = ([ "ab" : 1 ,
                        "cd": 2 ,
                        ])
标准格式：
        mapping 变量名 = ([
                                key1 : value1 ,
                                key2 : value2 ,
                                ..........
                                ..........
                        ]);
其中key可以是除了int以外的任何值！！ (好象char也不行吧 :P )，包括任何数组
甚至mapping也可以做key ,而value则可以是任意所有值。mapping的应用在于：
可以用key来index得到相应的value值。如上面eg的，我们有：a["cd"] == 2,....
因为要用key来index的关系，在mapping中key值不能相等，若相等则覆盖掉原来该
key对应的value。写的有些乱了，SORRY :P

    那么一个mapping怎么改变它的值呢？有两个方法(下面设mapping map )。
1. map[key] = value;
2. map += ([key : value ]);
请大家留意第二种赋值方法，它跟我们将要提到的数组的方法一样的 。
在mapping中“删除”一个key （其相应value当然也没了）方法是：
        map_delete(map,key);
lpc里面的系统函数(efun)的命名一般按参数顺序来取名（ map_delete的参数就是

map在前，被delete的key在后的 ），希望大家注意。
另外mapping的efun还有values,keys,undefinedp
大家可以直接在MUD里 help efunname 来看帮助，也可以参阅 /doc/lpc/types/mapping
这一文件

