[wizs] 副本(instance)簡易使用手冊

作者: laechan (揮淚斬馬雲)   2022-10-13 18:18:28
為了避免自己再忘記後不曉得怎麼使用,所以寫這個手冊。
/std/new_ob/instance_room.c 副本區域房間樣本檔
/std/new_ob/instance/ 主目錄
/std/new_ob/instance/satin.c 單純的 npc,裡面備註了日後的星際傳送門 npc
/open/cmds/cmd_instance.o 指令 instance 的儲存檔
/std/new_ob/instance/laechan/ 我所寫的所有副本(包含一些範例)
// instance_ob.c
// 副本物件繼承用樣本檔
// instance.c
// 副本區域房間樣本檔
// instance_rooms.c
// 副本房間暫存處,本物件不 update
上面三個都不用看,因為 simul_efun questing("instance" 已寫好
// _instance.c
// 副本管理指令
只需要看這個指令,以及 laechan/ 目錄下的 005.c 副本範例。
/open/cmds/cmd_instance.o 資料結構
"副本所在房間" : ([ "歸類/副本" : ({ ({"開放的難度":0=單/1=多人,}),
({"開放的難度":0=單/1=多人,}), ... }),
.
.
]),
instance(副本管理指令)說明:
============================================================
instance -list 列出副本管理員所在地的副本情報
instance -enter [副本編號] 進入所選擇的副本
也就是說在 instance 指令內就包含了如何從副本 A 跳去副本 B
的程式碼。
1.部分副本進入前設有等級種族職業等限制。
2.部分副本進入前須通過防機測試。
這些都可以在跳過去之前寫在 questing 之外。
管理者指令區
instance -export 匯出目前設定總表
instance -export here/房間完整路徑檔名 匯出指定資料設定
instance -add 房間完整路徑檔名 = 副本識別id 難度 單人/多人
instance -del 房間完整路徑檔名 = 副本識別id 難度 單人/多人
範例
instance -add /d/wiz/room/disc = laechan/001 normal single
instance -del /d/wiz/room/disc = laechan/001 normal single
instance -add /d/wiz/room/disc = justinj/002 hard1 multi
============================================================
> instance -export
副本設定總表:
副本所在房間 歸類 副本名 難度 單/多人
instance -add /d/ppl/map/n6e4 = ppl_area/ina_tiger hard1 single
instance -add /d/ppl/map/n6e4 = ppl_area/ina_tiger hard1 multi
instance -add /d/ppl/map/n6e4 = ppl_area/ina_tiger normal single
.
.
指令內容,enter 的部份:
單人副本
questing("instance","create",tmps[n3*2-2],({files,({me}),
tmps[n3*2-1][0],tmps[n3*2-1][1]}));
多人副本
questing("instance","create",tmps[n3*2-2],({files,
tmps2,
tmps[n3*2-1][0],tmps[n3*2-1][1]}));
亦即進入副本只需呼叫 questing("instance","create",
tmps 資料結構即 ({"難度",0/1,"難度",0/1,...})
所以玩家假設選第一個,tmps[n3*2-2] = tmps[1*2-2] = tmps[0] = "歸類/副本"
tmps[n3*2-1] = tmps[1*2-1] = tmps[1] = ({"難度",0/1})
files = 副本所在房間,即主 key
也就是說,如果要從我的工作室直接進入副本:
> l
[/u/l/laechan/workroom ]
= 天上界 =
明顯出口有: 無
Lv255.副本管理員─綾兒(Instance manager, Satin)
> instance -list
綾兒: 這裡可 enter 的副本如下:
==========================================
1.白瓦鎮黑熊討伐任務副本(普通單人模式)
2.測試串接副本(普通單人模式)
3.測試串接副本(普通多人模式)
==========================================
> instance -export
副本設定總表:
instance -add /u/l/laechan/workroom = laechan/001 normal single
instance -add /u/l/laechan/workroom = laechan/006 normal single
instance -add /u/l/laechan/workroom = laechan/006 normal multi
則 running code 可以這樣寫
questing("instance", // 呼叫副本處理
"create", // 呼叫創建(進入)副本
"laechan/001", // 副本名稱
({
"/u/l/laechan/workroom", // 副本所在房間
({me}), // 傳自己進去
"normal", // 難度
0, // 單人
}));
me->force_me("look");
========== 程式執行區 ==========
副本載入中......ok!
[/std/new_ob/instance_room#652352 ]
[副本]山林小徑
你撥開草叢, 隱約地可以看見埋在雜草堆裡面的道路, 由此可以
想見這條小徑平時應該沒有什麼人在走動. 據說小徑可以通往這
座山的深處, 可是蠻危險的.
明顯出口有: north.
========== 程式執行區 ==========
這樣就可以實現從副本 A 跳到副本 B 的設計,不過這是以後的
事,重點是接了某任務,在解任務過程中需要把玩家傳進副本,
這時候 files 的部份就很微妙,關鍵在於有些副本是不允許玩
家任意進入,只有在解任務解到某一步驟時才能進去。
== 分隔線 ==
再來需要副本的房間循環判斷部份
:::::::::::::: /std/new_ob/instance/laechan/005.c ::::::::::::::
// 005.c
// [email protected] add in 2014/04/26
// 模擬幻想○域的地獄裂痕副本
instance_data=([
"002":(["instance_check":1,
"cant_go":(["north":1]),
]),
即需要循環判斷就增設 instance_data 並給 instance_check = 1
int instance_check(string files,object room)
{
int flags,t;
object ob;
flags=(int)room->query("instance_flags");
t=time();
這個 flags 並不需要事前設定,最初值都是 0(即無)
switch(files)
{
// 西爾克交待任務
case "002":
switch(flags)
{
case 0:
birth_npcs(room,INSTANCE_NPC,"sealker");
instance_set(room,
({
"instance_data/enter_msgs",
({HIW"西爾克:你就是...來幫助我的人吧!"NOR"\n",
HIW"西爾克:請幫助我打倒礦坑內的魔物吧!"NOR"\n",}),
"instance_next_times",12+t,"instance_flags",1,
}));
break;
case 1:
if(t>room->query("instance_next_times"))
{
instance_del(room,({"cant_go/north"}));
instance_set(room,({"instance_flags",2,"already_ended",1}));
}
break;
}
break; // end of 002
採雙層 switch 設計,針對不同的房間、不同的 flags 階段,
來產生不同的結果。
剛進入房間時,它會產生 西爾克 這隻 npc:
instance_set(room,
({
"instance_data/enter_msgs",
({HIW"西爾克:你就是...來幫助我的人吧!"NOR"\n",
HIW"西爾克:請幫助我打倒礦坑內的魔物吧!"NOR"\n",}),
"instance_next_times",12+t,"instance_flags",1,
}));
並跑出對話,同時設定觸發下一 flags 階段所需的時間 12 秒,
並把階段參數 flags 改成 1。
則 12 秒過後
case 1:
if(t>room->query("instance_next_times"))
{
instance_del(room,({"cant_go/north"}));
instance_set(room,({"instance_flags",2,"already_ended",1}));
}
移除不能往北走的設定,並把 flags 改成 2(實際上無 case 2),
並加上 already_ended 參數,則 times_check 看到該參數就不會
做動作。
嘛,這樣就沒問題。我剛在從頭看了一次,重點在於
1.副本檔要先寫好
2.然後決定要把它設定在哪個房間
3.然後就能讓玩家進去玩了
我最近若有空,會拿毒竹城區域(即影子傳說區域)來測試,測試若
成功,毒城城會改為副本區域,好處是
1.這會是一個在其它 mud 沒看過的新設計
2.它可以模擬任天堂的影子傳說,設定難度關卡
3.如果這個能成功放出來,那吞食天地二也可以具現化
==== 分隔線 ====
副本檔案的 instance_check 函數無作用,原因不明。
檢查 /open/cmds/quest/quest_data/quest_data.c 690 行起
if(s>=4)
ob->create_instance(vars[0],vars[1],vars[2],vars[3]);
else if(s==3)
ob->create_instance(vars[0],vars[1],vars[2]);
else
ob->create_instance(vars[0],vars[1]);
參數給足四個,因此會呼叫副本檔案的 create_instance 函數
檢查 /std/new_ob/instance/instance_ob.c
它會呼叫底下
// 最後再把房間資料設到 /std/new_ob/instance/instance_rooms.c
ob->ppl_instance("set",ppl_name,setting_data);
檢查 /std/new_ob/instance/instance_rooms.c 的 ppl_instance 函數
case "set":
ppl_instance[ppl_name]=setting_data;
tmps=keys(ppl_instance[ppl_name]);
heart_beat_obs-=({0});
foreach(tmp in tmps)
if(tmp[0..0]!="#" && ob=ppl_instance[ppl_name][tmp])
heart_beat_obs+=({ob});
break;
我剛檢查的結果,副本房間都有被丟進 heart_beat_obs 裡頭
int heart_beat()
{
int i,j;
object ob;
j=sizeof(heart_beat_obs);
for(i=0;i<j;i++)
{
if(!ob=heart_beat_obs[i]) continue;
else if(!ob->query("already_init")) continue;
else if(ob->query("already_ended"))
{
heart_beat_obs[i]=0;
continue;
}
// 有 instance_check 才做 check_instance
// if(ob->query("instance_check"))
ob->check_instance();
}
所以我搞錯了,必須要有 already_init 但是不能有 already_ended
,一但出現 already_ended,instance_rooms 就會將其移除。
這時一種可行的方法,就是只要重新把它加回去即可,即
// 確定 room 有 already_init
INSTANCE_ROOMS->heart_beat_obs_func("add",room);
但基本上還是沒用,因為不明原因 already_ended 都會被加上去,
以 grep 搜尋的結果,這東西出現在以下幾個地方
/std/new_ob/instance_room.c
int init()
{
if(!query("already_init"))
{
string instance_ob,msg;
if(instance_ob=query("instance_ob"))
{
set("already_init",1);
// laechan add in 2015/02/22
// 沒有 instance_check 的情況直接加上 instance_ended
if(undefinedp(query("instance_data")) ||
undefinedp(query("instance_data/instance_check")))
set("already_ended",1);
if(msg=catch(call_other(instance_ob,"init_instance",this_object(),ppl)))
write(msg+"\n");
}
}
這意思是,雖然一開始全部房間都被加進 heart_beat_ob,但是
進入某房間後,如果該房間沒有 instance_data 資料或是沒有
instance_data/instance_check 資料,那就設定 instance_ended
,接著 heart_beat 發現它有 instance_ended 時就會將其移出
心跳判斷。
對 instance_room.c 除錯的結果:
> instance -enter 2
副本載入中......ok!
debug:
already_init=0
already_ended=0
instance_data=UNDEFINED <= 這裡是有問題的
ppl=玩家(laechan /std/user)
將 init 內 catch 的段落挪到前面:
debug:
already_init=1
already_ended=0
instance_data=([ "instance_check" : 1 ])
ppl=玩家(laechan /std/user)
已可正常識別出資料,bug fixed。
Laechan

Links booklink

Contact Us: admin [ a t ] ucptt.com