[心得] 房間等物件的程式碼,越單純越好

作者: laechan (揮淚斬馬雲)   2023-02-21 16:47:49
以下是依據 coding 經驗。
舉例的話,比方 wear 在大部份 mud 都是玩家指令,而不是
特別寫在防具上頭、或是防具樣本檔裡頭的如下形式:
void init()
{
add_action("wear_it","wear");
}
===========分隔線===========
例如說,蠻多的 mud,wiz 在撰寫一座城鎮的某防具店時,他
可能是這樣寫的:
inherit SHOP; <= 商店房間樣本
void create()
{
::create();
seteuid(getuid(this_object()));
set("short","瑪卡村─防具店");
.
.
set("obj_num",([
MAKA_EQ+"plate":10,
MAKA_EQ+"shield":10,
.
.
]));
call_out("load_obj",2);
}
以前這樣子寫沒什麼不好,但最近碰到了問題。
我 coding 的 mud,房間檔是藉由程式生成的,是說如果我撰寫
特別的機制,比方它要生成 050.c 這個防具店房間檔時,讓它
讀取如上的內容去做寫入,而其它的 001.c~099.c(略過050.c)
的房間檔,則讀取原本就寫好的樣本檔,那也是可以的。
但通常遇到的情況是,當我某一天想更改地圖檔,以致於 050.c
不再是防具店,而可能變成 049.c 才是防具店時,我就得去動不
只一個檔案。
那於是興起的一種想法是,有沒有更好的做法?
答案是有的,那就是將防具店會用到的指令,全部寫成玩家指令
例如說,buy 這個指令,實際上玩家在任何的地方都可以下,但
是唯有在特殊的地方,buy 才有作用:
ppl=this_player();
room=environment(ppl);
// 當玩家所在的房間並不是商店時
// 這裡對商店的唯一定義就是有 obj_num 這個資料
if(!room->query("obj_num"))
return notify_fail("什麼?\n");
同樣的,list、sell、identify、.. 這些我認為,也應該寫成玩
家指令。
這時會產生的問題就是,比方 list,嗯,不是只有商店才會用到
list 啊,任務店也會啊,那任務店沒有 obj_num,讀取的比方是
像 quest_list 這樣的參數,那怎麼辦?
ppl=this_player();
room=environment(ppl);
if(room->query("obj_num"))
{
執行相對應的商店 list 程式段;
return 1;
}
else if(room->query("quest_list"))
{
執行相對應的任務店 list 程式段;
}
.
.
// 剩下的情況就是一般房間
return notify_fail("什麼?\n");
這時形成的概念就是,以往由各種繼承檔來形成的各種形形
色色房間的情況,改成從現在開始,所有房間一視同仁,全
部房間的程式檔內容近乎相同,改成以底下的東西來區別這
些房間的差異:
一、描述。例如[防具店],[這是村子裡唯一的防具店,..]
以打怪的區域來說,大部份房間的名稱是一樣的,例如
[洞穴內的道路],只有極少數例外。
二、被額外設定的參數。例如 obj_num、quest_list、..
所謂被額外設定的參數,指的就是當以程式自動產生某個區
域的所有房間之後,該區域會有控制房,假設是 control.c
,它負責「設計」哪些房間應該 set 哪些設定:
// 房間用途與房號對應定義區
#define ARMOR_NUM 050
// 第一區: 設定房間名字
switch(room_num)
{
case 001: room->set("short","村子入口"); break;
case ARMOR_NUM: room->set("short","防具店"); break;
.
.
}
// 第二區: 設定房間敘述
switch(room_num)
{
case ARMOR_NUM: room->set("long",@LONG
這裡是村子裡唯一的一間防具店,你看到老闆通常都悠閒地坐在
櫃台的後方打盹兒,只有當客人上門的時候,才會慵懶地起身打
招呼。
LONG
);
}
break;
}
// 第三區: 設定額外資料
switch(room_num)
{
case ARMOR_NUM: room->set("obj_num",([
MAKA_EQ+"plate":10,
MAKA_EQ+"shield":10,
]));
break;
}
這樣當玩家進到 050 這間房間時,它就會看到不同於其它房間
的名稱、敘述,而在 050 被設定了 obj_num 的前提下,它在這
個房間就可以使用 list、buy、sell .. 等玩家指令。
好處有以下幾個
一、對區域的 coding 不再是一個房間一個房間去做依用途不同
的程式檔撰寫。不用寫房間了,房間靠程式讀取像是地圖路
  線資料後來產生。(連出口都自動設好了)
二、對 control.c 區域檔的諸多設定,就變得有點像 control.c
是這個區域的各房間細部設計稿,在裡面對各種不同用途的
房間做各種不同的 room->set() 動作。
三、這時,搭配已寫好的各種玩家指令,各個房間就自然依設定
  賦予了用途。將來假設改了地圖、新增或減少了房間,只需
要改 control.c,例如 050 變成了 049
#define ARMOR_NUM 049
再啟動一次房間生成,這樣 049 就會被 control.c 設定防
具店該有的資料。
最大的好處,就是大多數人通常懂設定,但不見得懂程式。而這
個 coding 環境可讓只懂設定的人也能參與 control.c 的內容設
計。理論上這個人懂設計,就能生成區域。
簡單的說就是
之前
我直接寫防具店的程式檔,裡面也已寫好該有的程式碼,包
含 set("short", set("long", set("obj_num",..
當玩家進來防具店、或是這個防具店被載入時,它就是一間
防具店。
以後
所有的房間都透過程式讀取地圖檔及房間樣本稿來生成
之後我透過 control.c 做像是設計稿的 design 工作
然後唯有玩家進入像 050 這個房間,此時它呼叫control.c
control.c 才依設計稿對 050 做「它是一間防具店」的諸多
設定(set)。或是這個房間被載入時,才做設定。
這樣子的好處就是,每一座區域的特殊房間資料,都在 control.c
裡頭,一目瞭然,區域作者或未來接手的人,想瞭解這個區域的資
料時,只要看該區域的 control.c 就知道了。
(這樣理論上 control.c 也能生成 QC 用的文件)
缺點則是,對於已經存在許久的 mud,就不好這樣子做了,所以比
較推薦,新的 mud 可以採取這樣的做法。以房間來說,盡可能讓
每一個區域房間的程式檔內容是單純的,然後透過像是玩家指令的
東西,來區別玩家在不同用途的房間時可以執行的動作為何。
第二個缺點則可能是玩家可用指令會變得賊多,但我是認為,符合
直覺的指令再多都沒關係,比方 buy sell list 這些。現在電腦
效能很好了,只要 driver 支援,就不用太顧慮 rehash 問題。
以上一點分享。GPT 早五年出來,我或許就會架一個這樣的 mud。
作者: tsetsethatha (吉星麥造~~~我來了)   2023-02-24 15:48:00
感謝l大惠賜心得文,收錄於精華區z-3-10-34 :)

Links booklink

Contact Us: admin [ a t ] ucptt.com