□  如何建造一個房間

    房間是構成這整個世界的要素之一，在此我們提供了一個房間的標準物件來讓
所有的房間繼承。而如同其他的物件一般，你需要寫一個 create() 來設定房間中
的敘述、出口、物品、生物等等。這裡，我喜歡說你用 create() 這個函式來賦予
這個房間的屬性。一般來說，要建造一個簡單的房間，你只要賦予它基本的屬性即
可。當然，我們不認為一個區域中幾十個房間沒有任何的機關或秘密，是個會吸引
玩家一遊的好地方。

    下面，提到了一些建造房間所需要留意的事項，也會配合一些例子來說明。

  一、基本篇

    一個基本的房間，要有 short <短敘述> 、 long <長敘述>、 exits <出口>

ˇ  當你在寫一個房間的 long <長敘述>時，其格式為：

	set("long", @LONG

房間的敘述.......

LONG
	);
      
    其中 @LONG 和 LONG  是互相對應的，你可以用任何字接在 @  後面，但是前
後兩個字一定要一樣，這樣系統才能判別，而房間的敘述寫完時，一定要換行後再
接第二個 LONG ，且同一行不能再有其他任何的字元，不然系統無法判定敘述是否
該結束了，會造成編譯時的錯誤。

    而為求區域看起來外觀上整齊、統一，房間的長敘述中每一行的長度必須一樣
，而一行的長度建議為 29 到 32 個中文字，約佔螢幕的三分之二。並且一個房間
的敘述最好不要低於三行，區域各個房間的敘述重複性降到越低越好，這樣你的區
域看起來才不會太過陽春。當然，有時候為了某些目的，比如一個迷宮，你可能會
相鄰的幾個房間都用到一樣的敘述，那自然不在此限。

ˇ  一個房間的出口則以下列格式賦予：

	set("exits", ([
		"方向"		: "連接到的房間之檔名",
		...........
	]);

    在這裡，為了一個以後區域開放後搬移目錄的便利性，建議採用__DIR__ 這個
由系統提供的巨集來寫路徑，比如說：

		"west"		: __DIR__"path3",
	和
		"west"		: "/u/d/davidoff/goathill/path3",

是完全一樣的。但前者顯然在以後目錄的搬移上方便的多。而在下面會提到設定房
間中的物品或生物時，也建議採用這種方式寫作。

ˇ  item_desc 這是用來設定個別景物的描述，當玩家用 look 這個指令時就會作
    用。其格式為：

	set("item_desc", ([
		"景物名稱"	: "景物敘述",
		...........
	]);

    其中景物敘述可以是字串或是一個 function ，所以你可以利用這個功能加以
變化，當玩家 look 一個景物時，可能看到敘述，也可能發生一些特殊的事件，而
你就可以在被呼叫的函式中寫下這些事件。

ˇ  objects 可以讓這個房間在每次 reset時載入某些生物或某些物品：

	set("objects", ([
		"物品或生物的檔名"	: 數量,
		...........
	]);

    如同前面所提到的，建議採用 __DIR__來編寫你的路徑，而數量則要用整數。
 
ˇ  要為這個房間添上門戶時，記得前面必須先 #include <room.h>。而格式為：

	create_door("出口方向", "門的名稱", "進入方向", 預設狀態);

    比如說，這裡明顯的出口有 west、east 和 up。 而你要讓西邊有一個關上的
紅木門，你可以這樣寫：

	create_door("west", "紅木門", "east", DOOR_CLOSED);

    當玩家進入這個房間時，他會看到：

	這裡明顯的出口有 east 和 up。

    而當他 look west 時，會看到：這個紅木門是關上的。

 
    其他的一些屬性，你可以參考 /doc/build/room_prop 或是讀一下標準物件的
room.c。也建議你可以多用 more here來觀看一間特殊的 room。

  二、進階篇

    要讓你的區域中富有變化，生動有趣，除了文字敘述的豐富度以外，你更可以
利用 init() 這個函式為你的房間增加一些「機關」或「秘密」。

    這裡，先讓我們瞭解一下 init() 的用途為何，和為什麼要用到它。每一個房
間的 create() 只有當 reset時才會被呼叫到，而 init() 則是在 B物件進入到 A
物件時都會呼叫到 A物件的 init() 。看到這，你應該可以看出差別了，我們希望
當一個物件（此處較多是玩家）進到一個房間時，能夠經由某個動作啟動這個房間
的機關的話，自然是利用 init() 來編寫。

    一般的使用方式，是在 init() 中利用 add_action() 來呼叫你寫的函式，其
格式為：
	add_action("function type", "action");

    function type 即是被呼叫的函式名	action 是啟動的動作

    而你就可以將被 action 啟動後要發生的事，都寫在被呼叫的函式裡面。理論
上來說，利用這個方式我們可以做到任何事，當然，能不能達成就看寫程式的功力
了。下面舉個簡單的例子：

void init()
{
        add_action("do_pick", "pick");
}

int do_pick(string arg)
{
        object me;

        me = this_player();
        if ( !arg || ( arg != "flower" ) ) return notify_fail("你要摘什麼？\n");

        else if ( random((int)me->query("kar")) < 7 )
                message_vision("$N將花摘了下來，但一不小心被刺了一下。\n", me);

        else
                message_vision("$N摘下一朵美麗的血紅色鮮花。\n", me);

        return 1;
}
   
    當玩家利用 pick 這個指令時就會呼叫到 do_pick() 這個 function，而啟動了
這個房間的機關。

    這裡特別提到一點，一個簡單的 room 我們為了使記憶體的使用量降到最低，會
在 create() 最後加上一行 replace_program(ROOM); 。這是因為在房間的標準物件
中有定義了如 init() 等其他的函式，而一個簡單的房間根本沒有用到，所以我們用
replace_program() 來將原本的被繼承的標準物件「重置」（或說取代）掉，但是一
旦房間中用到了 init() 來編寫時，就絕對不可以用 replace_program()，因為系統
屆時找不到 init() 便會隨便呼叫一個記憶體中的位址而隨便傳進一些亂七八糟的東
西，情況嚴重時，甚至可以讓整個 mud crash。但是，我們自不可因噎廢食，該用的
時候還是要用，這些應該是一個好的程式寫作人員自己必須留意的，發生狀況要自己
負責。

  三、建議

    這裡我們提供了一個工具來讓巫師們可以方便的編寫一個房間，那就是房間編輯
器(Roommaker) ，你可以 clone /obj/roommaker 來使用它。一般的步驟是，先利用
mkroom來造一個空房間，然後利用 goto 這個指令到房間裡去，再用 rset short 和
connect 來設定這個房間的短敘述及出口，而像其他的一些屬性例如 outdoors 等等
也都可以利用他來做到，接著用 to rset long 來設定這個房間的長敘述，最後再用
saveroom將這個房間存檔。要是這只是一個基本的房間，那到這裡就大功告成了，要
是妳想讓這個房間富有變化，那就再用線上編輯器 edit 或將這個房間的檔案 ftp
回去繼續修改。

Davidoff@ES2 (10-04-95')