发信人: xgw (似的看法), 信区: Mud
标  题: 巫师入门
发信站: 广州网易 BBS (Thu Mar 26 21:34:05 1998), 转信

                           巫师编程 (1)

1) 最简单的房间的撰写

最简单的房间是什么？就是里面什么都没有，机关，人物(npc)，物品，什么都没有
比如说就是一条路上的一
程序举例如下：（//表示注释）
// Room: /d/snow/eroad1.c      //程序开头注一下,说明是什么文件,在什么地方
inherit ROOM;                  //继承 ROOM 类
void create()           //  创建函数;在里面定义各种属性

        set("short", "黄土小径");      // 房间的 短描述
        set("long", @LONG              // 房间的长描述
这是一条普通的黄土小径，弯弯曲曲往东北一路盘旋上山，北边有
一间城隍庙，往西则是雪亭镇的街道。     //@LONG&LONG是一对宏,表示里面是字符串
 LONG
        );
        set("exits", ([ /* sizeof() == 3 */     // 设房间出口
  "northeast" : "/d/snow/eroad2",               // 不同的方向指令,跳转到
  "north" : "/d/snow/temple",                   // 不同的房间
  "west" : "/d/snow/sroad1",                    // 注意,不加 .c 后缀
]));
        set("outdoors", "snow");                // 是雪淳镇的室外
        setup();                                //不用管它,照抄就是
        replace_program(ROOM);

2)  房间的物品和门
最简单的房间说完了,就再复杂一点儿加上 物品定义,和物品拿枋?还有建个门吧
还是举例说明
// inn.c
#include
inherit ROOM;
string look_sign(object me);            // 函数原型定义
void create()

        set("short", "饮风客栈");
        set("long", @LONG
你现在正站在雪亭镇南边的一家小客栈里，这家客栈虽小，却是方圆
五百里内最负盛名的一家，客栈的主人据说是一位云游四海的仙人，如果
你的福缘深厚的话，也许可以在这里遇到他。除此之外，来自各地的旅人
都喜欢聚集在这里交换旅途上的见闻，你也可以在这里打听到许多有趣的
消息。靠近门口的地方有一块乌木雕成的招牌(sign)。
    西北边有一个红木门(door)，门上泛著一层白色的光晕，一种令人感
觉很有力量，又很柔和的光芒。
LONG
        );
        // This enables players to save this room as their startroom.
        set("valid_startroom", 1);        //使该房间可以成为下一次进来的地方
        set("item_desc", ([
                "sign": (: look_sign :),        // 物品(其实不是物品)描述
// 只是一个记号而已,玩家可以用 look sign 来看你的描述 , 不是实际存在的物品
//   冒号左边的是名称,右边的是描述 如 :
//  "pillar" : "石柱上刻著：剑气指天、剑心内敛、剑芒不显、剑神如电。"
// 玩家 look pillar 是,就会出来 后面跟的话
// 如果你想叫玩家look 时,去调一个函数,就后面跟一个 (: 函数名 :)
                "door": (: look_door, "northwest" :),
                // 这里调的就是look_door函数,参数是northwest
        ]) );
        set("exits", ([
                "east"          : "/d/snow/square",
                "up"            : "/d/snow/inn_2f",
                "northwest"     : "/d/wiz/entrance" ]) );
        // 下面是定义房间里的NPC, 这就是一个店小二啦
        set("objects", ([
                "/d/snow/npc/waiter" : 1 ]) );  // 后面的1, 就是一个的意思
        // 创建房间的门, 平时是关闭的
        create_door("northwest", "木门", "southeast", DOOR_CLOSED);
        setup();
        // 建一个公告板,是另一个地方定义的函数
        // To "load" the board, don't ever "clone" a bulletin board.
        call_other( "/obj/board/common_b", "???" );

// look sign时调用,根据玩家的级别返回描述
string look_sign(object me)     // me是一个对象,指向动作的主人
                                // 这里的me是参数,如果没有这个参数,
                                // 可以在函数里用
                                // object me; me = this_player();定

        if( wizardp(me) )       // wizardp(me) 是一个efun, 是mudos 定义的
                                // 判断me这个对象是否是巫师
                return "招牌写著：饮风客栈。旁边一排小字：庄思哑题。\n";
        else
                return "招牌写著：饮风客栈。\n";

3)  人物创作入门
每一个人物都需要单独的一个*.c文件,由其所在房间的源码调用.前面已经说过
一般来说,NPC放在npc各个地方的npc目录下,如 /d/snow/npc下是雪淳镇的npc
记住一点就是:NPC其实和玩家是一样的,只不过是电脑控制而已,
        玩家有的各项值它都有,而且比如说,fight的时候,它也会涨经验值的
        也会慢慢恢复. 所以它可以用command来执行命令.
举例说明:
::::::::::::::
fist_trainer.c
::::::::::::::
// trainer.c
inherit NPC;            // 继承 NPC类
void create()

        set_name("李火狮", ({ "trainer", "lee" }) );    //设NPC名字和中文名字
                               ^^^^^^^^^|^^^^^
                                        +-------就是有两个名字,哪个都可以
        set("title", "拳法教练");       // 头
        set("gender", "男性" );         // 性别
        set("age", 28);                 // 年龄
        set("str", 26);                 // str, int 都是天赋值
        set("int", 14);
        set("long",                     // 人物的长描述,当玩家look trainer时
                                        // 显示该描述
"李火狮是个孔武有力的大块头，他正在训练他的弟子们习练「柳家拳\n"
                "法」(liuh-ken)。\n" );
        set("combat_exp", 3000);        // 人物的实战经验
        set("attitude", "heroism");     // 好战态度
        set("force", 120);              // 内力值
        set("max_force", 120);          // 最大内力
        set("force_factor", 1);         // 一次发出多少点内力
        set("inquiry", ([               // 玩家ask 时的反应
                "here": "这里当然是淳风武馆，不然还是哪里？\n",
// 如上句, 玩家ask trainer about here 就会出后面的话
                "name": "在下姓李，名字就叫火狮，人称李教头的便是我。\n",
                "柳家拳法": "哦....说来惭愧，小弟这套拳法还没学得到家,
柳馆主就教我在这里传艺。\n"
        ]) );
        set_skill("unarmed", 30);       // 设置各项武功技能
        set_skill("liuh-ken", 20);
        set_skill("dodge", 30);
        map_skill("unarmed", "liuh-ken");       // enable 柳家拳法
        setup();

int recognize_apprentice(object ob)     // 接受玩家拜师时的反应

        if( (string)ob->query("family/family_name")=="封山剑派" ) return 1;
                        ^^^^^^^^^^^^|^^^^^^^^^^^^
                                    +---------------查询玩家的派别
        command("say 对不起，这位" + RANK_D->query_respect(ob) +
                                     ^^^^^^^^^^^|^^^^^^^^^^^^
                                                +---对象ob, 就是玩家,的头衔
"，您不是我们武馆的弟子。");
        return notify_fail( "李火狮不愿意教你拳法。\n");
               ^^^^^|^^^^^
                    +-------这是一个很有用的函数,就是给出错误信息

int accept_fight(object me)     // 接受玩家fight时调用

        if( (string)me->query("family/family_name")=="封山剑派" ) {
                command("say 进招吧。");
                return 1;
        }
        command("say 馆主吩咐过，不许和来这里的客人过招。");
        return 0;

4) 几个零散的问题
*******************************************
1. NPC怎么有货物？
*******************************************
        如店小二，在create()函数里定义
        set("vendor_goods", ([
                "匕首": "/obj/example/dagger",
                "酒": "/obj/example/wineskin",
                "包子": "/obj/example/dumpling",
                "鸡腿": "/obj/example/chicken_leg",
        ]) );
        增加一个list功能
        在init()里定义
void init()             // 当一个玩家进入同一环境时,调用

        object ob;
        ::init();
        if( interactive(ob = this_player()) && !is_fighting() ) {
            ^^^^^^^^^^^^^^|^^^^^^^^^^^^^^^     ^^^^^^|^^^^^^^
                          +---判断玩家是否存在       +---相互是否处于战斗状态
               remove_call_out("greeting");
                call_out("greeting", 1, ob);
        }
        add_action("do_vendor_list", "list");   //增加一个list的动作,
                       // list时自动调do_vendor_list,是系统函数

*************************************************
2. 进门时,自动问好
*************************************************
init()中函数设置如上
另外写一个greeting函数
void greeting(object ob)

        if( !ob || environment(ob) != environment() ) return;
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^判断是否是同一个房间
       switch( random(3) ) {  // 随机问好
                case  0:
                        say( "店小二笑咪咪地说道：这位" +
                                RANK_D->query_respect(ob)
                                + "，进来喝杯茶，歇歇腿吧。\n");
                        break;
                case 1:
                        say( "店小二用脖子上的毛巾抹了抹手，说道：这位" +
                                RANK_D->query_respect(ob)
                                + "，请进请进。\n");
                        break;
                case 2:
                        say( "店小二说道：这位" + RANK_D->query_respect(ob)
                                +"，进来喝几盅小店的红酒吧，这几天\
                                才从窖子里开封的哟。\n");
                        break;
        }

********************************************
3. 平时随机动作和战斗中动作
********************************************
在create()函数里
        set("chat_chance", 25);         // 随机动作概率 25%
        set("chat_msg", ({
                "僵尸护法说道：我们茅山派实力最强！谁敢欺负我们？\n"
                "僵尸护法对你说道：小心我把你变成僵尸! \n",
                "僵尸护法对你念了一会咒，你不禁毛骨悚然起来...\n",
                (:random_move :)        // 随机移动
        }) );
        set("chat_chance_combat", 70);  // 战斗中随机动作概率
        set("chat_msg_combat", ({
                (: command("你敢惹我，追到天涯海角我都要杀了你！") :),
                (: cast_spell, "drainerbolt" :),
                (: cast_spell, "netherbolt" :),         //使用各种法术
                (: cast_spell, "feeblebolt" :),
                (: cast_spell, "invocation" :),
        }) );
********************************************
4. 根据玩家询问的信息作相应的动作
********************************************
杨掌柜为例:
在create()函数里:
        set("inquiry", ([
                "治伤": (: heal_me :),
                "疗伤": (: heal_me :),
                "开药": (: heal_me :),
        ]) );
再定义一个heal_me函数
string heal_me(object me)

        int ratio;
        ratio = (int)me->query("eff_kee") * 100 / (int)me->query("max_kee");
        if( ratio >= 100 )
                return "这位" + RANK_D->query_respect(me) +
                "，您看起来气色很好啊，不像有受伤的样子。";
        if( ratio >= 95 )
                return
        "哦....我看看....只是些皮肉小伤，您买包金创药回去敷敷就没事了。";

作相应的动作
*****************************************
5. 人物身上的物品和钱
*****************************************
在create()函数里
__DIR__是一个宏,表示当前目录,提高程序可移植性
        carry_object(__DIR__"obj/magic_book");  // 一般的物品
        carry_object(__DIR__"obj/spells_book");
        carry_object(__DIR__"obj/thin_sword")->wield(); // 武器可以装备
        carry_object(__DIR__"obj/pink_cloth")->wear();  // 衣服等可以穿上
        carry_object(__DIR__"obj/shoe")->wear();
        add_money("gold", 3);           // 钱用add_money函数
--
********************************************************************************
******
********************************************************************************
***
                     巫师编程 (2)


5)  人物的属性 (scratch)
"id"                                                            (string)
    人物的识别字，这个字可以用来识别玩家，通常 "id" 跟 "name" 都不直接用
    set() 设定，而是用 F_NAME 中的 set_name()。
"title", "nickname", "name"                                     (string)
    人物的称号、绰号、与中文姓名。
"age"                                                           (int)
    人物的年龄，玩家的年龄是由定义在 USER_OB 中的 update_age() 定期更新。
"age_modify"                                                    (int)
    这个数值会在 update_age() 中被加在人物的年龄上，可以是负数。
"gin", "eff_gin", "max_gin"                                     (int)
"kee", "eff_kee", "max_kee"
"sen", "eff_sen", "max_sen"
    人物的精气神数值及其上限，其中 "gin", "kee", "sen" 请透过定义在 F_DAMAGE
    中的 receive_damage() 与 receive_heal() 增减，"eff_gin", "eff_kee",
    "eff_sen" 则用 receive_wound() 与 receive_curing()。
"str", "cor", "int", "spi", "cps", "per", "kar"                 (int)
    人物的天赋，依序分别为膂力(strength)、胆识(courage)、悟性(intelligence)
    、灵性(spirituality)、定力(composure)、容貌(personality)、福缘(karma)。
    根据天赋的原意，请勿修改这几个值，也不要依赖这些值做一些相关重大的检查。
"atman", "max_atman"                                            (int)
"force", "max_force"
"mana", "max_mana"
    人物的灵力(atman, AP)、内力(force, FP)、法力(mana, MP) 及其上限，可
    以用 set(), add() 直接修改，但是必须自行检查上下限。
"MKS", "PKS"                                                    (int)
    人物杀死其他生物的统计(MKS = Monster Killing Statistic, PKS = Player
    Killing Stastics)。
"combat_exp"                                                    (int)
    人物的实战经验?
"score"                                                         (int)
    人物的综合评价。
"force_factor"                                                  (int)
    人物 enforce 指定的强度，表示用内力伤人所耗的内力。
"mana_factor"                                           (int)
    人物 enchant 指定的强度，表示引出武器魔法效果所耗的法力。
"family"                                                        (mapping)
    人物的师承门派等记录，当人物拜师或自创门派时就会设定这个属性，其内容
    及定义如下：
        "family_name"： 门派名称
        "master_name"： 师父的中文名字
        "master_id"：   师父的英文代?
        "generation"：  人物在这个门派中的辈分，1 表示开山始祖，2 表示
                        第二代弟子....等等。
        "title"：       人物在这个门派中的地位，如"掌门人"、"左护法"、等
                        等，刚加入一个新门派的预设值是"弟子"。
        "priv"：        人物在门派中的权限旗号，一般弟子是 0，掌门人是 -1
        "enter_time"：  人物加入门派时 time() 所传回的值。
"guilds"                                                        (mapping)
    人物所参加的各种组织，每个组织有一个 identity 当 key，value
    的内容由该组织的相关物件自行定义。
"startroom"                                                     (string)
   人物 login 时出现的房间，一般都设为公会。

6)  如何建造一个房间
    房间是构成这整个世界的要素之一，在此我们提供了一个房间的标准物件来让
所有的房间继承。而如同其他的物件一般，你需要写一个 create() 来设定房间中
的叙述、出口、物品、生物等等。这里，我喜欢说你用 create() 这个函式来赋予
这个房间的属性。一般来说，要建造一个简单的房间，你只要赋予它基本的属性即
可。当然，我们不认为一个区域中几十个房间没有任何的机关或秘密，是个会吸引
玩家一游的好地方。
    下面，提到了一些建造房间所需要留意的事项，也会配合一些例子来说明。
  一、基本篇
    一个基本的房间，要有 short <短叙述> 、 long <长叙述>、 exits <出口>
□  当你在写一个房间的 long <长叙述>时，其格式为：
        set("long", @LONG
房间的叙述.......
LONG
                "west"          : __DIR__"path3",
        和
                "west"          : "/u/d/davidoff/goathill/path3",
是完全一样的。但前者显然在以後目录的搬移上方便的多。而在下面会提到设定房
间中的物品或生物时，也建议采用这种方式写作。
□  item_desc 这是用来设定个别景物的描述，当玩家用 look 这个指令时就会作
    用。其格式为：
        set("item_desc", ([
                "景物名称"      : "景物叙述",
                ...........
        ]);
    其中景物叙述可以是字串或是一个 function ，所以你可以利用这个功能加以
变化，当玩家 look 一个景物时，可能看到叙述，也可能发生一些特殊的事件，而
你就可以在被呼叫的函式中写下这些事件。
□  objects 可以让这个房间在每次 reset时载入某些生物或某些物品：
        set("objects", ([
                "物品或生物的档名"      : 数量,
                ...........
        ]);
    如同前面所提到的，建议采用 __DIR__来编写你的路径，而数量则要用整数。
□  要为这个房间添上门户时，记得前面必须先 #include 。而格式为：
        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。但是，我们自不可因噎废食，该用的
时候还是要用，这些应该是一个好的程式写作人员自己必须留意的，发生状况要自己
负责。
 7)  定义在人物物件中的附加函数(apply functions)
void defeated_enemy(object victim)
当这名人物打昏一个敌人的时候会呼叫这个附加函数，victim 即是被打昏的的
敌人物件。
呼叫者: COMBAT_D
有预设定义此一函数的系统物件: none
void killed_enemy(object victim)
当这名人物杀死一个敌人的时候会呼叫这个附加函数，victim 是即将要被杀死
的敌人物件。
呼叫者: COMBAT_D
有预设定义此一函数的系统物件: none
int accept_fight(object who)
当有其他生物对这个人物下 fight 指令的时候，会呼叫这个附加函数，who 是
下 fight 指令的生物，如果是对 player 下，"fight" 指令会直接显示讯息徵
求被挑战一方的同意，如果是对 NPC 下这个指令，则当这个附加函数传回 1
时才会接受挑战，否则显示某某不想跟你较量的讯息。
呼叫者: "fight" 指令
有预设定义此一函数的系统物件: NPC
int accept_object(object who, object item)
当有人用 give 指令给这个非玩家人物东西时，会呼叫这个附加函数，传回 1
表示愿意接受这个东西，传回 0 表示不接受。
呼叫者: "give" 指令
有预设定义此一函数的系统物件: none
void recruit_apprentice(objct apprentice)
当一个人物收了另一个人物做弟子时会呼叫这个附加函数，你可以在这个函数里
修改弟子的 rank 或其他东西。
呼叫者: "apprentice" 指令
有预设定义此一函数的系统物件: none
8)  房间属性
"short"                                                         (string)
    房间的短叙述。
"long"                                                          (string)
    房间的长叙述。
"item_desc"                                                     (mapping)
    房间中个别景物的叙述，格式为：([ <景物名称>:<景物叙述>, .... ])。
    其中<景物叙述>可以是字串或 function type。
"exits"                                                         (mapping)
    房间的出口，包括有门的方向，格式为：([  <出口>:<房间档名>, .... ])。
"objects"                                                       (mapping)
    房间中的物品、生物，格式：([ <物品或生物档名>:<数量>, .... ])。
"outdoors"                                                      (string)
    房间是否为「户外」，户外房间可以看到天色变化与气候影响。字串的意义
    表示房间的气候区，通常和该区域的 domain (即 /d 下的目录名称) 同。
"no_fight"                                                      (int)
    房间是为禁止作战区域。
"no_magic"                                                      (int)
    房间是为禁止施法区域。
9)  物品属性
"name"                                                          (string)
    物品的中文名称。
"id"                                                            (string)
    物品的英文名称。
"long"                                                          (string)
    物品的详细描述。
"value"                                                         (int)
    物品的价值，单位是「钱」(coin)。
"unit"                                                          (string)
    物品的单位，如：个、把、枝.....
"no_get"                                                        (int)
    表示物品是否可被捡起来。
"no_drop"                                                       (int string)
    表示物品是否可被丢弃，如果型态是 string, 则当有人企图丢弃这个物品时
    会将该字串用 notify_fail 传回去。
□ 武器属性
"skill_type"                                                    (string)
    这个武器所属的技能种类，如 "sword"、"blade" 等，要注意在 /d/skill
    下必须有一个定义该武器技能的物件，否则装备这个武器战斗时会有错误
    讯息。
"rigidity"                                                      (int)
    武器的硬度，当使用武器相斗时，硬度、武器的重量、持用者的力量将会影
    响武器受损的机率。
    武器受损机会所用的强度值计算公式如下：
        重量/500 + 硬度 + 力量
"weapon_prop"                                                   (mapping)
    武器对持用者的状态影响，通常武器只影响 "damage"，这些状态影响会在
    武器被装备时用 add_temp() 加到持用者的 apply 上，并於卸除或 dest
    时减回来。
"flag"                                                          (int)
    武器的一些型态旗标，详见  的定义。


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

                         编程中要注意的小问题

1.一些重要的单词可别写错喔：象create,environment,gender,......

2.LONG 描述的要点: 这是很重要的，一旦出错就很惨了.....
请注意：long <长叙述>，其格式为：

        set("long", @LONG

room or npc 的叙述.......

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

3.一些结构要有始有终，象函数的{ } 啦，还有mapping([ ]) ，array({ }) 啦，
要是忘记写后面的那个就.......

4.__DIR__ 的用法，一般来说__DIR__ 是指该NPC OR ROOM 的当前目录。所以要弄
什么物件的话就用这个__DIR__的相对目录做好了。

嗯，就想起这么多了，以后再写别的吧。

-
********************************************************************************
******
********************************************************************************
***

                         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 。而格式为：

        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
这一文件


