基礎 LPC
作者: Descartes of Borg
第一版: 23 april 1993
第二版: 16 june 1993
第二章: LPC 程式
2.1 關於程式
這一章的名字取得不怎麼好, 因為沒有人用 LPC 寫程式. 寫 LPC 程式的人寫
的是物件 (objects). 這兩種說法有啥差別 ? 好吧, 就我們現在的目標來說,
差別在於兩者檔案執行的方式不同. 當你「跑」一個程式的時候, 都是從程式中
固定的地方開始執行. 換句話說, 就是所有的程式開始執行的時候, 一定有個地
方寫清楚要從那裡開始. 另外, 程式有一個固定的終止點, 所以執行程式只要執
行到該終止點, 程式就中止執行. 總之, 程式從固定的開頭跑到固定的結尾.
LPC 物件就不是這麼一回事.
在 mud 裡面, LPC 物件只是遊戲 (driver) C 程式中, 顯而易見的部分. 換句
話說, mud 程式在 driver 裡面開始與結束執行. 但是實際上, 對於創造你玩的
mud 世界來說, driver 並沒有做多少事. 反之, driver 相當依賴 LPC 碼, 並
需要執行物件中的程式碼. 所以 LPC 物件不需要有起始點, 也不需要有固定的
終止點.
就像其他的程式語言, LPC 「程式」可以由一個或一個以上的檔案組成. 很簡單,
程式要先載入 driver 的記憶體. driver 會根據本手冊所教的結構, 讀取物件
中一行行的程式. 有一件重要的事你要先搞清楚, 就是 LPC 物件執行時沒有開
頭也沒有終止.
2.2 diiver-mudlib 之間的互動
我先前提過, driver 是在主機上執行的 C 程式. 它讓你連上遊戲, 並執行
LPC 碼. 注意, 這是 mud 程式設計的一個理論而已, 也不需要比其他的方法好.
整個 mud 遊戲可以全部用 C 來寫. 這樣遊戲的執行速度快上很多, 卻讓 mud
缺乏可塑性, 使巫師在遊戲正在執行的時候無法加入新東西. DikuMUD 就是全部
用 C 寫成的. 相反的, LPMUD 的理論就是 driver 不該決定遊戲內容, 而遊戲
內容應該決定於遊戲中的個別事物, 並能夠在遊戲執行時加上東西. 這就是為什
麼 LPMUD 使用 LPC 程式語言. 它能讓你用 LPC 定義遊戲內容, 交給 driver
依需要讀取並執行. 況且學 LPC 要比 C 容易得多, 這樣讓更多人能加入創造
世界的過程.
一旦你用 LPC 寫了一個檔案 (假設是用正確的 LPC), 它只是躺在你主機的硬
碟裡不動, 直到遊戲中有東西參考 (reference) 它. 當遊戲中有東西終於參考
到它時, 這個檔案就會被複製一份到記憶體裡面, 並且呼叫這個物件中一個特殊
的函式 (function). 呼叫這個函式的目的是初始化 (initialize) 這個物件中
的變數. 現在, 別管你腦袋裡才看到的上兩句話, 因為一個對程式設計完全陌生
的人來說, 哪裡會知道函式或變數到底是啥東西. 現在重要的是要知道 driver
讀取主機硬碟裡面的物件檔案, 複製一份之後扔進記憶體儲存 (既然是複本, 也
就可以有許多不同的版本 ). 你稍後會知道什麼是函式、什麼是變數, 並搞清楚
到底遊戲中的一些東西是怎麼參考你的物件的.
2.3 將一個物件載入記憶體
雖然一個物件裡面並沒有規定要從一個固定的地方開始執行程式, 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.