编程终究是一件技术性的活,我们总不能一直通俗下去吧!在这一章,我们该开始回过头来,对我们工作系统里的一些基本的概念进行一一的理解,当然要接触一些深一些的概念了。
通常我们在大陆所见到的中文MUD大多是LpMUD,一般是一些角色养成型的游戏模式。LpMUD使用Unix的指令和文件结构。而很多版本也是运行在Unix环境下(目前WINNT的版本也不少)。
&&--理解LPC
LPC是什么东东?就是写LPMUD程序的C语言啦!它看起来和一般的C语言区别不大,语法基本一样,只是简单得多啦,可以说,它是我所见到的最简单的一种C语言。他们的根本不同点在于,Lpc程序是编写一个一个的"Object"。 这有什么区别呢?一般的程序是在执行过程中,通常有一个明显的开始和结束。程序从一个地方开始,然后顺序执行下去,到了结束的地方就中断了。而Lpc的Object不是这样的。Lpc的Object可能没有明显的开始和结束的标志,它可能永远在工作。在有些游戏中,整个游戏包括Driver和游戏世界都用C写好,这样处理的速度能快一些,但是游戏的扩充性很差,巫师们不可能在游戏进行中添加任何东西。 LpMud则相反。Driver理论上应该和玩家所接触的世界几乎没有任何直接的联系。游戏世界应该是自己独立的,而且是“即玩即加”的。举个例子,我们在玩三国志系列时,在你没能取得赤壁之战的胜利之前,你是不可能去打六出祁山的,甚至连四川都去不了,但是在MUD游戏中,却可以任由玩家选择,而且巫师也可视其需要,在任何时候、在任何地方再加上一场另外的战役。所以,在你写完一个Lpc的文件时,它就存于主机的硬盘上。在游戏进行中,当需要整个Object时,Driver就从硬盘中读入这个文件,然后放在内存中,一个特殊的函数被调用来初始化这个Object的一些变量。当然这要建立在这个文件没有任何错误的基础上。否则,轻则出错,重则宕机。
在这里,既然我们不断谈到“变量”和“函数”,则有必要谈谈在LPC中它们之间的关系。变量就是一个能够变化的值,函数通常是用来操纵那些变量的一段程序。Lpc 的Object就是一些变量和函数的组合。在前面我们讲过,Object没有开始、也没有结束的地方,更不会有一个特别的地方让Driver去执行它。那当一个Object如果要被内存中的另一个Object调用时将怎么办呢?Driver一般会去找这个Object的那堆变量放在哪里。如果这些变量没有值,那么Driver就会调用一个特定的函数,即create()来初始化这些变量。(看到这里,你可以回去看看第二章的有关部分)。
但是要强调一点,create()并不是 Lpc代码开始执行的地方,只是大多数的Object从这里开始。事实上,create()是可以不存在的。如果这个Object不需要对变量初始化,那么create()可以不存在。那么这样的Object开始执行的地方就完全的不同于一般的Object,它可以从任何地方开始。所以在Lpc的Object中,函数的排列顺序是无所谓的,随便那个排在前面对这个Object的特性没有影响。只不过在各个MUD中的巫师品质要求中稍作规定而已。
&&--理解Object
既然LPC中最不同的就是Object,因此,当你想在程序中设计任何动作时, 都应当要考虑到这个动作是哪一个 object 所做的, 不然很容易导致错误。LPC 的语法并不严谨, 有些场合为了省事可以将函数是由哪个Object所做的省略掉, 例如我们在 create() 函数中最常看到的 set(),事实上严谨的写法应为this_object()->set()。因此我们首先要学习的就是如何利用系统提供的函数,去正确快捷地寻找Object。
MUD系统为我们提供了两个最好用的两个函数this_object()与this_player(),在你写作一个对象(房间、物品......)时,this_object()表示自己这个对象(房间、物品......),也就是你写的这个文件本身啦。在这里,要提及一下object的所谓封闭性。每一个object有自己独立的数据结构,它能与其他object严格区分开来。即使是同一个长剑程序,在同一个房间里你复制了两把时,在内存中就会有两个完全独立的长剑object,你对其中一把无论进行改名、改威力都不会影响到另一把的数据,这就是object的封闭性。而this_player()则比较复杂, 它会传回一个属于玩家类型的对象。这个玩家在init中就是触发init的那个玩家。this_player()会跟著函数呼叫一直传递给所有被init呼叫的函数, 包括add_action中所定义出来的函数, 在这些函数中, 它又表示做动作的那个人。
除此之外,当我们只知道一个对象的名字,而不是它的文件名时,是无法用个 object 类型的变量指向它,这时我们就要用到[present() 函数]
用法:
object=present(string "id",object env)
函数在此时就从名字(id)找到这个object,有了这个object才好对它做操作。就可以派上用场。简单的想, present 函数其实就是在一个房间或者一个人身上(房间与人对于程序其实是一样子的)里找出叫某个名字的物品的函数,它是同类型找物品的函数中最有用的一个, 其余的函数还有:find_player(), find_living() 等等
再看一组很实用的函数:[environment(),first_inventory(),next_inventory(), all_inventory()]。这一组函数跟对象所处在的位置有关:
environment(object ob)传回了对象 ob 所处在的地点。假如 ob 是个玩家或生物,那么这个函数会传回 ob 所在的房间;如果 ob 是个物品,是有人带的就传回携带着 ob 的生物, 没人带的话就传回 ob 所在的房间。
first_inventory(object ob) 所传回的是 ob 中的第一个对象,如果 ob是房间,则传回这个房间中的第一个物品或是生物;如果 ob 是生物, 则传回他身上所带的第一个物品。
next_inventory(object ob) 通常是跟着 first_inventory() 一起使用,它的功用是传回 ob 的下一个物品。
很简单, all_inventory(object ob) 所传回的是包含了所有物品的一整个阵列。一个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];
&&--理解Efun
从上面你也许可以看到了,象environment()这样的各种函数,也许会在你编程时时不时地发现,而且用的地方特别地多,最常见的就是this_player()、this_object()、还有strcmp()、implode(),左看右看找不到在哪里定义的,而你就算是找遍你下载下来的单机版的所有文件,都找不到这些函数,其实它们就是efun。efun就是MUD外部定义的函数,也就是externally defined function 的缩写。是由Mud Driver定义好的。它是以计算机直接能理解的二进制的形式存在着的,所以它们执行起来要比一般的Object带有的函数速度快的多。而对于Object内部定义的函数,通常叫作lfun(local function)。一个巫师的主要工作也就是编写一些lfun组成的Object。
那么为什么要创立efun呢?
1) 处理一些很常用的、经常会有许多函数会调用的。
2) 处理internet socket的输入输出。
3) 以及一些Lpc很难处理的事,因为毕竟Lpc是C的很小的子集。
efun是用C写好的,内嵌在Driver里面的。在Mud起来之前,和Driver一起编译好的,它们的调用和你写的函数的调用方法是完全一样的。总的来说,你只需要关心:它需要传入什么参数、将会返回什么的东西就行了。
通常在一个Mud里面,你可以在类似这样的/doc/efun的目录底下找到有关efun的说明和帮助,或者直接用help <efun名>指令就可以得到帮助。因为efun依赖于你所在的Mud的Driver,所以不同的Driver带有的efun区别很大。不要想当然地将别的MUD中的经验带到这里来,也不要在我们这里理解了某个efun,就自以为什么都懂了。对于一个新的巫师不说,你只需要简单地理解,并会使用一些常见的efun就行了