ES2 MUD LIB :: 東方故事二(ES2) 天朝帝國 mudlib 瀏覽展示
/doc/build/room
□ 如何編寫房間
如果你玩過 mud 的話﹐應該對 mud 組成一個世界的方式相當熟悉﹐沒錯﹐我們就是
用一個個的房間(room)來構成這個虛擬的世界﹐這裡我們稱「房間」只是傳統的 mud
的稱呼﹐因為早期的 mud 全名是 Multi-User Dungeon﹐Dungeon 就是「地牢」的意
思﹐因此用房間來稱呼地牢的最小單位而沿用至今。
當然﹐我們現在的 mud 早已脫離地牢的時代﹐而是一個沒有限制的創作空間﹐如何
將你想像中的人、事、地規劃成以房間為單位的舞臺﹐就看你的經驗和功力了﹐ 通
常 mud 並不硬性規定一個房間跟另一個房間的距離、位置等關係﹐而是根據實際需
要而決定房間的連結方式。不可否認的﹐大多數巫師在從事區域的創作時都會或多
或少加上距離跟位置的考量﹐但是在此仍然要提醒一下﹐為了位置跟距離的關係而
製作的房間是不具意義的。
建造房間﹐首先你必須先自己規劃一個「區域」的構想﹐我們通常把一個村莊、洞穴
、城鎮等地理上能夠明確區隔其範圍的單位稱為一個區域(area)﹐理想的區域設計﹐
是將所有該區域用到的程式放在同一個目錄﹐並且和其絕對路徑無關﹐可以隨時掛上
或搬移到其他的目錄下﹐因為區域是一個獨立的地理單位﹐具有一致的風格跟設定﹐
對於大多數 mud 的管理者而言都希望萬一因為區域的擴展而有需要移動或整理現有
區域時﹐能不必一個個地去修改檔案﹐能夠配合管理者要求的巫師﹐自然就會受到歡
迎。
當你有了構想之後﹐相信很多巫師會用「畫地圖」當成第二步﹐根據我的觀察﹐一個
用這種方法寫出來的區域通常會有太多不必要的房間﹐因為畫地圖時往往會不自覺地
將房間想像成「具有相同面積的方塊」﹐因而大量增加了房間的個數﹐你如果不信﹐
可以試著比較接下來我所建議的方法﹐看看哪一種方法會有比較多的房間。
我所建議的方法是「身歷其境」的創作方式﹐當你構想好一個區域大致的內容之後﹐
可以先寫出這個區域的入口﹐想像你是第一個「發現」這個地方的探險者﹐然後跟著
你的想像力所至﹐開一個出口到下一個房間﹐繼續探險﹐千萬要注意的是﹕
◎ 不要畫地圖﹐甚至想像中也不要用「空中俯瞰」區域的方式去設計你的區域﹐因為
一個探險者是很難有機會俯瞰他發現的區域的﹐mud 在空間概念上較傾向於冒險遊
戲而非角色扮演/模擬遊戲。
◎ 寫完一個房間的敘述再寫下一個﹐不要用 roommaker造出空房間之後就急著連接出
口到下一個房間﹐因為這樣到最後往往你會需要苦思半天才能想出一些無聊的敘述
填滿你用 roommaker造出來的一堆多餘房間﹐當你發現不知道要在一個房間的敘述
裡寫什麼的時候﹐就表示這個房間其實是多餘的。
◎ 讓你的想像力引導你的創造力﹐不要因為覺得一座城堡「應該」有四座塔樓﹐而寫
出你的故事中根本不必出現的場景﹐或者是因為一條長廊「應該」比國王的大廳長
很多而用五、六個房間來「堆砌」這樣的場景﹐要記得每間房間的存在都需要耗費
電腦的一些資源去儲存﹐浪費太多的資源在無意義的場景上將使你的區域在難以過
關。
◎ 適當地使用「動作」連接你的場景﹐例如有一棵樹可以爬上去﹐你可能開一個 up
的出口直接連到樹上的房間﹐這時候如果花點心思﹐讓使用者必須從環境的敘述中
發現提示﹐如樹旁消失的腳印﹐樹皮新近剝落的痕跡等引導玩家發現一個 climb
的動作﹐不是能使你的區域更有內容嗎﹖
不過要提醒一些懶惰的新巫師﹐就是像這樣 code 片段不要直接從一些範例中「
拷貝」下來﹐修改一下文字字串就到處適用﹐如何設計一個有趣的「動作」將是
你成為有實力的巫師最佳的鍛鍊方式。
好了﹐雖然上面囉裡囉唆地談了一大堆﹐想必還沒寫過區域的巫師可能已經一頭霧水
了﹐我們現在就來看看如何寫一個最基本的房間。
□ 簡單的房間
// example_room.c
void create()
{
set("short", "茅屋中");
set("long", "你現在正站在一間小茅屋中。\n");
setup();
}
上面這就是一間最簡單的房間了﹐set() 這個函數用來設定物件的一些屬性﹐short
和 long 分別是房間的簡稱跟敘述﹐當玩家用 brief 模式移動的時候﹐只會看到沿途
所經房間的簡稱﹐而不會看到完整的敘述。在有些用 roommaker 產生的房間中﹐你也
許會看到 @LONG .... LONG 的字串常數表示法﹐這是一項由 LPC 的提供的功能﹐格式
是﹕
@tag text
tag
開始一段 text block 的 tag 可以為任何 identifier (和 #define 格式一樣)﹐
而結束 text block 的 tag 跟開始 tag 需一模一樣﹐且位於一行的第一個字元﹐
例如﹕
set("long",
@TEXT This is a text example.
This is line #2.
TEXT);
相當於 set("long", "This is a text example.\nThis is line#2.\n");
setup() 是大部分物件在 create() 裡的最後一個步驟﹐這個函數通常由你 inherit
的物件所定義﹐呼叫這個函數可以讓你繼承的物件檢查你在 create() 裡所作的設定﹐
並補足不足或不合的部份。
在此要補充的一點就是﹕房間的 short 和 long 被玩家看到的過程是直接經由 "look"
這個指令處理的﹐和其他大多數物件、NPC 由 F_NAME 的 short()、long() 間接處理
的是有所區別的﹐你在房間中定義 short() 或 long() 無法改變玩家看到的敘述( 因
為房間根本不繼承 F_NAME )。
□ 其他房間屬性
在房間的 create() 中除了 short﹐long 之外﹐你還可以用 set() 設定許多其他的
屬性﹐以下是簡短的介紹﹕
◎ 出口
set("exits/north", "/d/snow/inn_hall");
可以設定房間 north (北方) 這個出口連向 /d/snow/inn_hall.c 這個檔案所定
義的房間﹐不過通常我們不鼓勵直接指定絕對路徑﹐因為萬一所連接的房間檔案
被搬動到其他地方﹐你就必須修改這個檔案﹐因此常用 __DIR__ 這個由 LPC 的
preprocessor 提供的巨集表示這個檔案所在的目錄( 通常我們要連接的房間都
在同一個目錄下 )﹐假設這個房間位於 /d/snow/ 下﹐上面的程式可以寫成﹕
set("exits/north", __DIR__"inn_hall");
當然﹐我們也可以一次設定很多個出口﹕
set("exits", ([
"north" : __DIR__"inn_hall",
"south" : __DIR__"room2",
"out" : __DIR__"subarea/entry",
]) );
這裡的 ([ .... ]) 是 LPC 的 mapping 型態常數表示法﹐在後面的屬性設定中
經常會用到。
◎ 物件
set("objects", ([
__DIR__"obj/stone" : 1,
__DIR__"npc/guard" : 2
]) );
上面的敘述會在這個房間裡放置一個由 __DIR__ + "obj/stone.c" 定義的物件跟
兩個由 __DIR__ + "npc/guard.c" 定義的 NPC﹐想必你已經注意到在這裡我們用
的是直接設定整個 objets 屬性的方式﹐而非 objects/xxxx﹐那是因為物件檔名
中的 '/' 字元如果放在 set() 的第一個參數﹐會被解釋成屬性路徑﹐因此我們
直接定義 objects 這個屬性為一個 mapping 型態的物件表﹐這和前面定義出口
屬性是有所不同的。
由 objects 所設定的物件會在 setup() 之後以及每次房間的 reset() 被呼叫後
補齊所設定的個數。
◎ 細部敘述
set("detail/樹幹", "這棵樹的樹幹又老又皺﹐像是八十歲的老太婆。\n");
這個屬性可以設定房間敘述中的細部描述﹐當玩家在這個房間裡下
> look 樹幹
的指令時﹐就會看到你所設定的敘述﹐我們使用中文做為參考細部敘述的關鍵字﹐
是希望讓謎題的難度更有挑戰性﹐而不要像以往一樣在房間敘述裡寫著「....樹幹
(trunk)...」使得玩家看都不看就知道要打 look trunk。
至於其他的屬性設定﹐請參考有關房間屬性文件目錄下個別屬性的文件。
□ 基本變化
上面所介紹的設定方法應該已經足夠你寫出第一個「普通」的房間了﹐但是那樣的程度
對 LPmud 來說只能算是幼稚園的程度﹐因為如果你夠聰明的話﹐很可能只需要看幾個
別人寫好的範例﹐而不必看上面的說明就可以寫出一樣水準的房間﹐接下來我們所要介
紹的是一些常見的變化。
□ 進階變化