這裡以 sanc 為例,sanc 穿脫的指令是
/cmds/std/_wear.c
/cmds/std/_remove.c
問題:如何不更動防具/武器繼承檔、不更動欲設定為套裝的
防具/武器檔案的情況下,實裝套裝設定?
解答:更改 wear/remove 指令
撰寫獨立的 /std/suit.c 套裝判斷檔(假設 define 為 SUIT)
在 suit.c 裡面可以宣告如下變數
mapping suit_data=([
"套裝名":([
"suits":({"武防檔名1","武防檔名2",..}),
"effect":(["屬性/效果1":屬性/效果值1,
"屬性/效果2":屬性/效果值2,
.
. ]),
]),
.
.
]);
mixed suit_files;
然後宣告幾個處理用的函數
void create()
{
string t1,t2;
// 在該物件載入時,讓它讀取 suit_data 來產生出 suit_files 資料
suit_files=({});
foreach(t1 in keys(suit_data))
foreach(t2 in suit_data[t1]["suits"])
suit_files[t2] ? suit_files[t2]+=({t1}) : suit_files[t2]=({t1});
}
// 判斷 ob 是不是套裝
int is_suit(object ob)
{
if(!ob) return 0;
if(undefinedp(suit_files[base_name(ob)])) return 0;
return 1;
}
那麼在 wear 時,針對正在處理的某物件,做如下判斷:
// 若該物件 ob 是套裝物件, 就執行 wear_suit
if(SUIT->is_suit(ob)>0)
SUIT->wear_suit(me,ob);
int wear_suit(object user,object ob)
{
string files,t1,t2,tt;
mixed tmps=({});
object tmp;
int n;
if(!user || !ob) return 1;
files=base_name(ob);
tmps=all_inventory(user);
foreach(t1 in suit_files[files])
{
n=0;
foreach(tmp in tmps)
{
if(!tmp->query("wear")) continue;
tt=base_name(tmp);
if(member_array(tt,suit_data[t1]["suits"])!=-1)
n++;
}
// 當 已裝備數量 = 該套裝實際數量 時
// 才執行 add_suit_effect
if(n==sizeof(suit_data[t1]["suits"]))
add_suit_effect(user,suit_data[t1]["effect"]);
}
return 1;
}
上面的意思是說,比方 wear all,然後它看到了正在裝備的是
套裝物件時,它會去算你目前已經裝備了某套裝的幾件裝備了,
唯有當 已裝備數量 = 該套裝實際數量 時,才執行下面函數:
int add_suit_effect(object user,mapping effect)
{
string eff
// 這裡假設所有的效果全部使用 add 的方式增加
// 比方 加 stat-str 50
// = user->add("effect/stat-str",50);
foreach(eff in keys(effect))
user->add("effect/"+eff,effect[eff]);
return 1;
}
remove 同理,同樣先做 is_suit 判斷,若該物件是套裝,就呼
叫 remove_suit 函數:
int remove_suit(object user,object ob)
{
string files,t1,t2,tt;
mixed tmps=({});
object tmp;
int n;
if(!user || !ob) return 1;
files=base_name(ob);
tmps=all_inventory(user);
foreach(t1 in suit_files[files])
{
n=0;
foreach(tmp in tmps)
{
if(!tmp->query("wear")) continue;
tt=base_name(tmp);
if(member_array(tt,suit_data[t1]["suits"])!=-1)
n++;
}
// 當 該套裝實際數量 - 已裝備數量 = 1 時
// 才執行 remove_suit_effect
if(sizeof(suit_data[t1]["suits"]) - n == 1)
remove_suit_effect(user,suit_data[t1]["effect"]);
}
return 1;
}
int remove_suit_effect(object user,mapping effect)
{
string eff
foreach(eff in keys(effect))
{
user->add("effect/"+eff,-effect[eff]);
if((int)user->query("effect/"+eff)==0)
user->delete("effect/"+eff);
}
return 1;
}
以上只是簡單寫一下,實際上依各 mud 的不同,寫法上會有所
差異,但概念是相通的。這樣做的好處有以下幾個:
1. 不管是用宣告 mapping suit_data 或是 save/restore 的方
式去儲存套裝資料,這些套裝的設定都會集中在同一個地方,
就方便做套裝資料的增刪改,可以統一管理這些資料。
2. 撰寫適當的指令給玩家,玩家就能自行瀏覽整個 mud 總共有
多少套裝,以及全部裝備上之後,會有什麼效果。
3. 不需要去動現有的武器/防具檔,也不需要動它們的樣本檔,
只需要動 穿脫武防具 的指令。
4. 讓 if(SUIT->is_suit(ob)>0) 的判斷先行,它是最簡判斷,
通過了,才執行複雜處理函數 wear_suit/remove_suit。
依現在電腦的處理效能及多工性,這些都是小 case。
5. 這寫法最大的好處,就是 一件武防 可同時是 A 套裝與 B 套
裝的內含武防。這個在 RO 很常見,即 A套裝 與 B套裝 都同
時包含了該武防。針對這一點,上面那些函數的判斷一樣有效
它唯一的缺點就是會增加 wear 與 remove 的處理複雜度,以及
SUIT 物件不能壞,我平常是不建議用 catch 去包相關的呼叫段
落,不過這也是可以考慮的