Mud 版 (精华区)

发信人: jiayb (多情剑客痴情剑), 信区: Mud
标  题: LPC编程入门
发信站: 哈工大紫丁香 (Wed Apr  1 17:59:43 1998), 转信

发信人: mice (风中的云), 信区: MUD
标  题: LPC编程入门
发信站: 冰城驿站之BBS (Fri Feb 27 20:47:24 1998), 转信

  这篇说明是为了新上任的巫师所写的, 我假设读著这篇说明文件的
新巫师已经读过 help new_wiz 中的内容, 并对巫师专有的指令如 clone,
update, cd, ls, cp... 等能熟练地加以使用, 但对如何开始写作自己
的区域感到茫然, 不知所措的新进巫师 

[在开始制作之前]

    让我们大略看一下在 LP MUD 中, 世界的构成方式 这个世界是由
                      (目前大陆上玩的 mud ,包括 xkx,fy,es2 都是 lp mud
                       即所谓战斗 mud。---lnwm 注。)
一个个的对象 (object) 所组成, 每个对象有一个对应的程序来描
           (比如你在mud里见到的每个房间,每个npc,每个物品,甚至
            你自己,都是一个 object,都是一段程序。----lnwm 注。)
述它的特性 我们可以藉由写作一段程序来创造出一个全新的对象, 可
以利用 update 来更新对象所属的程序, 用 clone  来实际造出一个可
       (比如你敲 update here,就是将你现在所在的房间更新,这里需要强调
        的是,所谓更新,就是将硬盘里这个文件编译后形成一段代码,这段代
        码是存在在内存中的。因此当你修改了一个房间,那只表明你在硬盘上
        改动了这个文件,你必须做一下 update ,将它编译放入内存,你的修
        改才正式生效。hehe,当然,编译有错误例外。----lnwm 注。)
用的对象 .
     (clone其实就是 update,只不过它update的是一个物品或npc,这个物品
        或npc还需要有地方放,所以clone就是 update+move。---lnwm注。)

    在这里, 我们有各式各样的对象, 但是可以将之区分为三大类: 房
间 (ROOM), 物品 (OBJECT), 与生物 (LIVING)  在我们制作区域的惯
               (这个OBJECT不过是个名词以和ROOM LIVING区分,和我们mud的
                object是两码事。---lnwm 注。)
例上, 我们习惯将房间的档案直接摆在区域的目录下, 生物与物品则摆
               (区域的习惯放置位置是根目录下的 /d 目录。特别是大陆的,
                你可以在 /d下找到所有的房间和npc-------lnwm 注.)
在这个区域中名为 npc 及 obj (也有人喜欢用 object 或 item
为名, 看个人习惯 ) 的子目录中 。

      以下我将以这三大类对象来分别说明该如何实作出区域

[如何制作房间]

   一个房间必定继承了 ROOM (inherit ROOM), 这是在
               (对,如果你要写一个房间,那么首先要做的就是在这个程序
                的开头写上
                       inherit ROOM;
                在有些时候,比如在xkx里,你 
             可能还要加上一句
                       #include <room.h>
                因为房间的门是定义在那里的。---lnwm注。)
mudlib.h中被 #define 好的一个档案, 它表示一个特别的档案, 所
有的房间都必须继承它. 才能拥有属於房间的一切特性 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^!

        一个房间有三个非常重要的函数: create(), init(), 与 refresh()
这三个函数会在某些特定的时机被系统所呼叫, 并且可以由你自行改写
, 以达成千变万化的效果 。

 [create 函数]

  create() 是房间在一被创造出来时必定要呼叫的一个函数, 通常我
         (如果你学过C语言,那么在lpc中,void create()
          就相当于 void main(),即主函数。---lnwm注。)
们都在里面做一大堆设定初值的动作 随便找一个房间来看, 我们可以
发现 create() 函数中总是有一大堆的 set("something", somevalue);
这些 set 的意义在此不详述, 你可以自己猜,  也可以问问较资深的巫
师 .
         (在我们这个mud中,你可以敲help STD_ROOM来看看这些设置是干什么
          的。---lnwm注。)
        有时你的房间并不直接继承 ROOM 而是继承了一个有继承 ROOM 的
特别房间, 像是商店或是公会房间什麽的 这时候你所写的 create()
         (在我们的mud中,钱庄就是个典型的例子,在钱庄的前面,你必须
          inherit SHOP;
          这个SHOP就是在ROOM的基础上加了钱庄的公用功能。---lnwm注。)

  [init 函数]

        init() 函数被呼叫的时机在於有生物 (npc及玩家)  进入这个房
         (这里的意思是,当有玩家或npc等活物进入房间时,可以是走进来,
          扔进来,或clone进来,房间中的这个init()函数就被触发,函数
          实现的功能就被实现。----lnwm注。)
间的时候 这时有个常用的函数 this_player()  会传回走进房间的这
个人, 或是npc。 this_player() 的概念容後再谈, 你现在只要记住这
个函数在每个生物走进来时都会被呼叫一次就可以了 
         (这里的意思是,如果你要对进入房间的玩家做一些动作,比如弄晕他
          或给他中毒等时,那么就在init()函数里对 this_player()作操作
          就可以了,this_player()就是触发init()函数的玩家。---lnwm注。)


       在 init() 中最常见的的函数莫过於 add_action("function", "action");
了 它的作用是在进来的生物身上添加上一个指令 (注意, 系统只认指
令的第一个字), 并在玩家下达这个指令时去呼叫 "function"  中所给
定名称的函数 举例而言, 如果我们写了这样的 init():

init()
{
      add_action("do_climb", "climb");
}

当玩家走进这个房间时, 系统会帮他多出 climb 这个指令 当他下达
                                    (如果这个指令不是cmds,那么在没有
                                     add_action的房间里就不会存在。)
了 climb tree 这个指令时, 系统会去寻找 do_climb() 这个函数, 并
                                   (do_clone()这个函数当然是你去写了
                                                       ---lnwm注。)
加以执行。 同时, 系统会将玩家所输入的 "climb"  这个指令後的所有
文字以字串类型的变量传给 do_climb()  你可以将 do_climb 这个函
数宣告为
         int do_climb(string arg) 
         (注意,这个带函数类型和参数类型的函数才是完整写法。---lnwm注。)

这样一来, 当玩家下达 climb tree, 或是 climb the red wall 这种指令时,
"tree" 或是 "the red wall" 就会被存进字串变量 arg 之中供你处理 。

        由 add_action() 所宣告的函数必定要是一个整数类型的函数, 因
为系统会根据这个函数的传回值采取不同的动作 如果你传回的是 0,
那麽系统会认定这个命令与你这个处理函数无关, 而对其他也有 climb
命令的函数一个一个尝试著去执行, 直到所有的 climb 命令都传回 0。

         (这段话很重要,当你要玩家在使用某个cmds时出现其他的用处,
          比如你要玩家eat一个果子不仅仅涨食物,而且涨别的东西,那么
          你就要在果子上用 add_action来给eat赋予更多的内容,然后在你
          处理eat的那个函数最后 return 0;
          这就是告诉系统,我这个指令并没有完,系统就会寻找本来的
          那个 eat 命令的功能函数,也就是加上食物。
          这称为重载。----lnwm注。)

        若你的函数传回值为 1, 表示这个指令已经由你所写的函数处理掉
了, 系统不会再尝试著往下面继续寻找其他的 climb 指令 
         (给出一个例子。
          void init()
          {
                add_action("do_eat","eat");
          }
"tree" 或是 "the red wall" 就会被存进字串变量 arg 之中供你处理 。

        由 add_action() 所宣告的函数必定要是一个整数类型的函数, 因
为系统会根据这个函数的传回值采取不同的动作 如果你传回的是 0,
那麽系统会认定这个命令与你这个处理函数无关, 而对其他也有 climb
命令的函数一个一个尝试著去执行, 直到所有的 climb 命令都传回 0。

         (这段话很重要,当你要玩家在使用某个cmds时出现其他的用处,
          比如你要玩家eat一个果子不仅仅涨食物,而且涨别的东西,那么
          你就要在果子上用 add_action来给eat赋予更多的内容,然后在你
          处理eat的那个函数最后 return 0;
          这就是告诉系统,我这个指令并没有完,系统就会寻找本来的
          那个 eat 命令的功能函数,也就是加上食物。
          这称为重载。----lnwm注。)

        若你的函数传回值为 1, 表示这个指令已经由你所写的函数处理掉
了, 系统不会再尝试著往下面继续寻找其他的 climb 指令 
         (给出一个例子。
          void init()
          {
                add_action("do_eat","eat");
          }

if (条件不合)
        return notify_fail(错误讯息);
if (另一个条件不合)
        return notify_fail(另一个错误讯息);
..............................
<所有可能导致错误的输入都过滤光了>
开始真正干活的部份....
return 1;
          (可以参考一下上面的例子。---lnwm注。)

    [refresh() 函数]

        refresh 呼叫的时机是系统定时 (约每半个小时一次) 呼叫 主
要的用途在於房间中npc 物品的再生 如果你改写了 refresh()
函数, 千万记得要串接 ::refresh(), 否则可能导致严重的後果 (门
一打开就不会自动关上, npc打死後也不会再生...

    由於有 set("objects", (["name1" : "file1", "name2" :
"file2",... ]) ); 这种写法的存在 (在 create() 里面这麽写) 所
               (set("objects",([]))用来在房间里放东西,比如
                npc或物品什么的。---lnwm注。) 
以 refresh() 被用到的机会不多了  (因为 set("objects", ) 这
个写法可以帮你作出自动定时 refresh npc 物品)  但是在制作一
些必须定时回复原始状态的小机关时, 仍然有必要用到这个函数 
     ( refresh 函数用到的地方并不是很多,因为它太僵硬,要十分
       固定的半个小时(准确的是24分钟)才被调用一次,可以用在
       一些周期较长的秘密中。---lnwm注。)

有关房间的部份就写到这里, 接下来是物品 


  [ 物品的制造]

        要制作物品, 首先必须 inherit OBJECT; 理由与做房间时必须
inherit ROOM 一样 OBJECT 是最基本的物品, 如果你要做的东西是武
器 防具 地图等, 你必须 inherit WEAPON, ARMOR, MAP 等等才能获
得这种类别的物品所拥有的特性

        物品的重要函数只有 create() 与 init(), 作用与 ROOM  中的同
名函数大致相同 

[create() 函数]
以 refresh() 被用到的机会不多了  (因为 set("objects", ) 这
个写法可以帮你作出自动定时 refresh npc 物品)  但是在制作一
些必须定时回复原始状态的小机关时, 仍然有必要用到这个函数 
     ( refresh 函数用到的地方并不是很多,因为它太僵硬,要十分
       固定的半个小时(准确的是24分钟)才被调用一次,可以用在
       一些周期较长的秘密中。---lnwm注。)

有关房间的部份就写到这里, 接下来是物品 


  [ 物品的制造]

        要制作物品, 首先必须 inherit OBJECT; 理由与做房间时必须
inherit ROOM 一样 OBJECT 是最基本的物品, 如果你要做的东西是武
器 防具 地图等, 你必须 inherit WEAPON, ARMOR, MAP 等等才能获
得这种类别的物品所拥有的特性

        物品的重要函数只有 create() 与 init(), 作用与 ROOM  中的同
名函数大致相同 

[create() 函数]
      房间上的 create() init()函数用法和房间相同,正如前面说的,可能
      有一点区别的就是物品的init()函数被触发的机会多一些而已。
                                        ---lnwm注。)

[npc]

        简单的npc很好做 NPC 也没有 refresh() 这个函数 

[程序必须的概念]

        你必须了解, 在 LPC 中最重要的一个概念是对象(object) 当你想
做任何动作时, 都要考虑到这个动作是哪一个 object 所做的, 不然很
容易导致错误 LPC 的语法并不严谨, 有些场合为了省事可以将函数是
由哪个对象所作的省略掉, 例如我们在 create() 函数中最常看到的
set(), 事实上最严谨的写法应为 this_object()->set() write() 则
为 this_player()->write() 
        (对,我们必须弄清楚,object 的概念是整个lpc的核心。
         往深一点说,每个object无非是一个包含了数据结构和内部
         外部函数的对象,他的数据结构决定了它的属性,比如房间的
         名称,玩家的skills等等,而他的外部函数提供了修改这些
         数据结构的方法,比如 set_skill("xkx",1000)
                         比如 set("title","大侠")
         这里set_skill()和set()函数就是object和外部的接口。
         数据结构和接口函数就拼出了一个object。
                                                        ---lnwm注。)

    说这麽多只是为了强调一件事: 你能抓出一个物品的 object 变量
就能让他干一切他所能做的事 

[this_object() 与 this_player()]

        这两个函数是系统所提供的函数, 也是最最好用的两个函数 在你
写作一个对象 (房间 物品...etc.)时, this_object() 表示自己这个
对象 
         (讲清object的结构后,你必须清楚object的所谓封闭性。
          当我们在程序里写 set("name","长剑")时,实际上是
          this_object()->set("name","长剑")的简略写法。
          这样set("name")的不是其他的东西,每一个object有自己独立的
          数据结构,它能与其他object严格区分开来。
          即使是同一个长剑程序,当你clone了两把时,在内存中就有两个
          完全独立的长剑object,你对其中一把做的任何处理如改名,改
          威力完全不影响另一把的数据,这就是object的封闭性。
                                 ---lnwm注。)


        this_player() 则比较复杂, 它会传回一个属於玩家类型的对象 
这个玩家在 init 中就是触发 init 的那个玩家 this_player() 会跟
著函数呼叫一直传递给所有被 init 呼叫的函数, 包括 add_action 中
所定义出来的函数, 在这些函数中, this_player() 就是表示做动作的
那个人 



[present() 函数]

        常常, 我们只知道一个对象的名字, 却不能用个 object 类型的变
量指向它
          当我们在程序里写 set("name","长剑")时,实际上是
          this_object()->set("name","长剑")的简略写法。
          这样set("name")的不是其他的东西,每一个object有自己独立的
          数据结构,它能与其他object严格区分开来。
          即使是同一个长剑程序,当你clone了两把时,在内存中就有两个
          完全独立的长剑object,你对其中一把做的任何处理如改名,改
          威力完全不影响另一把的数据,这就是object的封闭性。
                                 ---lnwm注。)


        this_player() 则比较复杂, 它会传回一个属於玩家类型的对象 
这个玩家在 init 中就是触发 init 的那个玩家 this_player() 会跟
著函数呼叫一直传递给所有被 init 呼叫的函数, 包括 add_action 中
所定义出来的函数, 在这些函数中, this_player() 就是表示做动作的
那个人 



[present() 函数]

        常常, 我们只知道一个对象的名字, 却不能用个 object 类型的变
量指向它

        first_inventory(object ob)  所传回的是 ob 中的第一个对象,
如果 ob 是房间, 则传回第一个物品或是生物, 如果 ob 是生物, 则传
回他身上所带的第一个物品 

        next_inventory(object ob) 通常跟著 first_inventory()  一起
使用 它的功用是传回 ob 的下一个物品, 在同一个 environment 中 

        all_inventory(object ob) 类似於 first_inventory(),  但是它
所传回的是包含了所有物品的一整个阵列 
        (上面谈的是环境问题,一个object(除房间外)都要有自己的环境,
         所谓环境就是这个object在什么地方放着,是一间房间还是一口箱子
         还是一个人身上,环境通常是另一个object。
         比如物品A和B放在一个人M身上,那么上面的函数就给出了它们的关系
         M=environment(A);
         M=environment(B);
         A=first_inventory(M);
         B=next_inventory(M);
         A=all_inventory(M)[0];
         B=all_inventory(M)[1];
[更进一步的提示]

    LPC 的函数群有三个, efun, lfun, simul_efun 它们提供了绝大
部分的功能。

[关於输出输入讯息的各个函数的提示]

can_read_chinese
printf, sprintf
scanf, sscanf
write, say, shout
tell_object, tell_room

[关於对象操作的函数]

clone, new                             (变出新object )
destruct, remove                       (摧毁object )
move, move_player, move_around         (移动object   ---lnwm注。)

              (这些函数不可能一一讲解了,你可以在mud里用help <函数名>
               来看(英文呦),然后再放到程序里自己试试。    ---hong 注。)


--
※ 修改:.yangx 于 Apr  1 17:42:42 修改本文.[FROM: 202.97.228.245]
※ 来源:.冰城驿站之BBS bbs.hr.hl.cn.[FROM: 202.118.226.90]

--
☆ 来源:.哈工大紫丁香 bbs.hit.edu.cn.[FROM: yangx.bbs@bbs.hr.hl.]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:216.732毫秒