这一章的名字取得不怎麽好, 因为没有人用 LPC 写程式. 写 LPC 程式的人写的是物件 (objects). 这两种说法有啥差别 ? 好吧, 就我们现在的目标来说, 差别在於两者档案执行的方式不同. 当你「跑」一个程式的时候, 都是从程式中固定的地方开始执行. 换句话说, 就是所有的程式开始执行的时候, 一定有个地方写清楚要从那里开始. 另外, 程式有一个固定的终止点, 所以执行程式只要执行到该终止点, 程式就中止执行. 总之, 程式从固定的开头跑到固定的结尾. LPC 物件就不是这麽一回事.在 mud 里面, LPC 物件只是游戏 (driver) C 程式中, 显而易见的部分. 换句话说, mud 程式在 driver 里面开始与结束执行. 但是实际上, 对於创造你玩的 mud 世界来说, driver 并没有做多少事. 反之, driver 相当依赖 LPC 码, 并需要执行物件中的程式码. 所以 LPC 物件不需要有起始点, 也不需要有固定的终止点.
就像其他的程式语言, LPC 「程式」可以由一个或一个以上的档案组成. 很简单, 程式要先载入 driver 的记忆体. driver 会根据本手册所教的结构, 读取物件中一行行的程式. 有一件重要的事你要先搞清楚, 就是 LPC 物件执行时没有开头也没有终止.
我先前提过, driver 是在主机上执行的 C 程式. 它让你连上游戏, 并执行 LPC 码. 注意, 这是 mud 程式设计的一个理论而已, 也不需要比其他的方法好. 整个 mud 游戏可以全部用 C 来写. 这样游戏的执行速度快上很多, 却让 mud 缺乏可塑性, 使巫师在游戏正在执行的时候无法加入新东西. DikuMUD 就是全部用 C 写成的. 相反的, LPMUD 的理论就是 driver 不该决定游戏内容, 而游戏内容应该决定於游戏中的个别事物, 并能够在游戏执行时加上东西. 这就是为什麽 LPMUD 使用 LPC 程式语言. 它能让你用 LPC 定义游戏内容, 交给 driver 依需要读取并执行. 况且学 LPC 要比 C 容易得多, 这样让更多人能加入创造世界的过程.一旦你用 LPC 写了一个档案 (假设是用正确的 LPC), 它只是躺在你主机的硬碟里不动, 直到游戏中有东西参考 (reference) 它. 当游戏中有东西终於参考到它时, 这个档案就会被复制一份到记忆体里面, 并且呼叫这个物件中一个特殊的函式 (function). 呼叫这个函式的目的是初始化 (initialize) 这个物件中的变数. 现在, 别管你脑袋里才看到的上两句话, 因为一个对程式设计完全陌生的人来说, 哪里会知道函式或变数到底是啥东西. 现在重要的是要知道 driver 读取主机硬碟里面的物件档案, 复制一份之後扔进记忆体储存 (既然是复本, 也就可以有许多不同的版本 ). 你稍後会知道什麽是函式、什麽是变数, 并搞清楚到底游戏中的一些东西是怎麽参考你的物件的.
虽然一个物件里面并没有规定要从一个固定的地方开始执行程式, driver 却要先找到一个固定的地方并执行之, 才能初始化一个物件. 在精简模式的 driver 上, 这是一个叫作 reset() 的函式. 在原始模式 mud 中, 则是 create().LPC 物件是由变数 (variable) 所组成的 (会更改的值) 而函式是处理这些变数的程式. 函式经由 LPC 语法结构来处理变数, 语法结构包括: 呼叫其他函式、使用外部定义函式 (externally defined functions, efuns)、基本的 LPC 运算式 (expression) 和流程控制 (flow control mechanism).
前面这些听起来乱七八糟的吧 ? 让我们从变数开始着手. 拿「等级」变数来说吧, 等级可以随情形不同而改变它的数值, 而不同的事物也使用玩家的等级数字作出不同的事. 举个例: 如果你是等级十九级的玩家, 则等级变数的数值就是 19 . 如果你的 mud 是旧的 LPMud 2.4.5 系统, 等级 1 到 19 级是玩家, 20 级以上是巫师, 则会有许多事物会询问你的等级变数值, 判断你能不能使用巫师的动作. 基本上, 任何 LPC 物件就是一堆会随时间不同而改变的变数组成的. 发生在物件身上的事, 都基於该物件的各个变数里头的数值. 而常常也有许多事会更改变数.
所以无论何时, 一个 LPC 撰写的物件被其他在记忆体的物件拿来参考时, driver 就寻找这物件里面所要找的值在哪里 (但是现在还没有任何数值) . driver 找过之後, 就呼叫物件中的 reset() 或 create() 函式 (视不同 driver 而定) , 来设定该物件一开始的变数值. 就这样, 经由「呼叫」「函式」处理变数.
虽然绝大多数的 LPC 程式码都从 create() 或 reset() 开始执行, 此处却不是 LPC 程式码开头的地方. 事实上, 没有这两个函式也没关系. 如果你的物件一开始所有的值都是 NULL (虚无) 指标 (在此, 虚无指标我们先当它是 0 吧) , 那你就不需要 create() 或 reset() 函式. 所以, 每个物件开始执行程式码的地方都可能完全不同.
现在让我们搞清楚这整章在讲些什麽. 问题是: 一个完整的 LPC 到底是由哪些东西组成的 ? 好, 一个 LPC 物件简单来说, 就是一个或一个以上的函式组合起来, 处理一个以上的变数 (或是不处理变数也行) . 各个函式之间完全不用管它们摆的先後顺序. 换句话说:
-----
void init() { add_action("smile", "smile"); }void create() { return; }
int smile(string str) { return 0; }
-----跟底下的一样:
-----
void create() { return; }_____int smile(string str) { return 0; }
void init() { add_action("smile", "smile"); }
另外有个很重要的事提醒你, 下面这个物件只有:
-----
void nonsense() {}-----这样也可以, 但是这种微不足道的物件, 它大概不会与你的 mud 中的其他物件作出正确的互动关系, 因为这样的物件没有重量、看不到......以此类推.
2.4 本章总结
LPC 码没有起点或终点, 因为 LPC 码是用来创造 driver 程式使用的物件, 而非单独的程式. LPC 物件包括一个或多个函式, 其间先後顺序毫无关系, 而这些函式之中, 处理多个变数 (或根本没有任何变数) . LPC 物件只是躺在主机的硬碟里面, 等着游戏中其他的物件参考它 (换言之, 它们实际上不存在) . 一旦一个物件被参考到, 它会被载入记忆体中, 并且它所有的变数都是零. 精简模式 mud 呼叫此物件的 reset() 而原始模式 mud 呼叫 create() (如果此物件有这些函式的话 ), 让这些函式来指定一些变数的初始值. 物件中的其他函式由 driver 或游戏中其他的物件使用之, 让物件之间达到互动并处理 LPC 变数.reset() 和 create() 的说明:
只有原始模式的 mud 使用 create() (请见本手册的 Introduction 一章, 有关原始模式和精简模式的介绍). 此函式仅用来初始化刚被参考的物件.
原始模式及精简模式的 mud 都使用 reset() 函式. 在精简模式 mud 中, reset() 有两个功能. 第一, 它用来初始化刚被参考的物件. 第二, 在精简模式的 mud 中, reset() 用来重新设定物件. 也就是说, 让物件回到最初的状态. 这样可以让一个房间内的怪物重生, 或把一道门关回去......以此类推. 原始模式的 mud 只用 reset() 作第二种功能 (就跟 reset 的意思一样).
所以在 LP 式的 mud 中有两件重要的事情让 driver 呼叫物件中的函式. 第一件事是创造物件. 此时, driver 呼叫物件中的一个函式来初始化物件的变数值. 在精简模式的 mud 里, 由 reset() 做此工作 (要加上 0 参数, 後面的章节再讨论参数是啥). 原始模式的 mud 下, 由 create() 做此工作.第二件事是把房间重新设定回某些基本的状况. 这些基本的设定可能会与一开始的初始值不同, 也可能相同, 而你当然也不想花时间一直去重覆做某些事 (像是重新设定一些不会更改的变数) . 精简模式的 mud 用 reset() 函式来创造和重新设定物件. 而原始模式的 mud 用 create() 创造物件, 用 reset() 重新设定物件. 但是精简模式也不会失去所有的变数值, 因为有个方法能区分是创造物件还是重新设定物件. 在精简模式要重新设定, 则 driver 传入 1 或重新设定的数字当作 reset() 的参数. 现在这个对你来说没啥意义, 但是要记住, 你在精简模式实际上是可以把两种情形区分开来的. 另外也要记住, reset() 在创造物件时传入的参数是 0, 而重新设定物件时传入非零值.
翻译: Spock of Final Frontier 98.Jan.16.