[閒聊] 任務「腳本」的設計原則
基本上,我本身比較不樂見看到「腳本」裡面有自訂函數的
程式段,因為這並不符合「腳本」的意義。
既然是「腳本」:
1.其構成必然簡單,不複雜
2.不應存在程式段落
換言之,現行的腳本,其實是「腳本」+「自訂函數」的綜
合體,簡單的任務就只有腳本,複雜一點的任務則再搭配自
訂函數這樣。
這是必須接受的事實。
然後一般任務進行時必然具備四個條件:
1.quest 的對象 stepX_npc
2.quest 前判斷 stepX_check
3.quest 中訊息 stepX_msg
4.quest 後結果 stepX_end
然後還需要知道一個函數:questing
所以 justin 你應根據上面,先判斷:
1.這四個條件以及該函數是否為基本足夠?
2.為何你的任務,無法使用現行的 quest 來做?
比方說,你希望玩家在任務步驟 2 跟 3 可以隨意決定順序
、並只需完成它們其中一個即可:
問題:nonseq 需不需要寫?
解答:需要。所以你建議了,我才會寫。因為根據內定流程
,你要接任務 3 就必須先接任務 2。
它是比 stepX_check 更優先的內定判斷。
將它作為內定判斷是因為大部份的任務都是如此。
問題:anyone 需不需要寫?
解答:不一定需要。因為它可以先用 nonseq 及 stepX_end
去模擬「你只要接了它們其中一個就代表接完全部」
那我為什麼寫?因為我評估無風險。
然後,「自定函數」其實是很簡單的東西,我們隨便 more
一個有自定函數的任務檔
:::::::::::::: /open/cmds/quest/l/l002.c ::::::::::::::
inherit "/open/cmds/quest/quest_d.c";
int step5_end(object ppl,object npc,string key);
int step4_check(object ppl,object npc,string key);
.
.
你需要瞭解的就是,stepX_check 函數就是「任務步驟結
束前的插斷判斷」,stepX_end 函數就是「任務步驟結束
後的附加執行」。結束前跟結束後的差異,就在於 steps
參數是否已經變更(玩家身上的),有變更就代表玩家已結
束該任務。
other=
int step1_check(object ppl,object npc,string key)
{
if(ppl->query("level")<120)
{
write("你的等級要滿 120 級才能接這個任務喔。\n");
return 1;
}
return 0; // 代表可接
}
int step1_end(object ppl,object npc,string key)
{
// 設定玩家接該任務的時間
ppl->set_temp("time_record/l002_times",time());
return 1;
}
##
比方你要設定任務步驟第 4-7 的各個接取條件,我認為,
用 stepX_check 及 stepX_end 及一些附加判斷即可具現
最後我要講的東西就是,比方某一個 stepX_check..
int stepX_check(object ppl,object npc,string key)
{
if(ppl->query("race")=="human" ||
((ppl->query("race")=="elf" && ppl->query("pri_guild")=="mage")) ||
(ppl->query("race")=="devil" && ppl->query("pri_guild")=="fister"))))
{
write("你的 bla bla ... 必須滿足條件喔,抱歉,我必須將你踢出。\n");
ppl->move_player("/d/wiz/room/disc","SNEAK");
return 1;
}
return 0; // 代表可接
}
為什麼我不將上面的東西用「腳本式設定」來處理?
因為沒效率。假設真的存在一種設定可以滿足上面,那麼
可以預知兩個結果:
一、腳本的該部份段落「會很像程式」,例如..
stepX_check=
race=human or bla bla bla##
那腳本化有何意義?
二、程式為了配合這樣的東西,「必須把所有的可能都納
入」,結果卻只是給極少數的任務設定極少樣性的條
件使用,何必呢?
我的意思就是,在「腳本」跟「程式」之間,我們實際上
需要取得的是一個有效率的平衡點。我並不反對將 quest
改的更理想,只是我要先讓你知道我在想什麼,這樣我們
改良的方向才會正確,這其中亦包含了 debug 的部份。
Laechan
--
※ 發信站: 批踢踢實業坊(ptt.cc)
※ 編輯: laechan 來自: 210.61.157.53 (12/18 10:09)
推
12/18 10:38, , 1F
12/18 10:38, 1F
→
12/18 10:38, , 2F
12/18 10:38, 2F
→
12/18 10:40, , 3F
12/18 10:40, 3F
→
12/18 10:42, , 4F
12/18 10:42, 4F
你可以把 quest read xxxx 後的 xxxx.c 檔內容前半貼
出來。規則是「跟 xxx.h 的定義法一樣」。
→
12/18 10:43, , 5F
12/18 10:43, 5F
→
12/18 10:44, , 6F
12/18 10:44, 6F
→
12/18 10:45, , 7F
12/18 10:45, 7F
→
12/18 10:46, , 8F
12/18 10:46, 8F
就我說的:用自定函數的做法。
我上面提過,如果玩家走 1,2,4,7,那你就在 stepX_end
(X=1,2,4,7) 都自訂,例如..
step1_end=
ppl->set_temp("time_record/xxxx_1",1)##
step2_end=
ppl->set_temp("time_record/xxxx_2",1)##
step4_end=
ppl->set_temp("time_record/xxxx_4",1)##
other=
int step7_end(object ppl,object npc,string key)
{
if(ppl->query_temp("time_record/xxxx_1") &&
ppl->query_temp("time_record/xxxx_2") &&
ppl->query_temp("time_record/xxxx_4"))
{
write("任務失敗。\n");
// 將任務移除要玩家重接
// 使其造成的結果相當於 quest del justin xxxx
questing("相關功能",bla bla...);
// 若該功能沒有就自己寫,或提出需求由我來改 quest_data_d.c
return 1;
}
return 1;
}##
※ 編輯: laechan 來自: 210.61.157.53 (12/18 10:56)
推
12/18 13:13, , 9F
12/18 13:13, 9F
→
12/18 13:14, , 10F
12/18 13:14, 10F
喔,這樣實在沒必要,不過也是可以...
這是因為 str = "路徑+檔名", 它是沒有 " " 的, 這時候
ROOT 就變成字串而不是代號。
如果你那時就舉這個例子,我就知道你的意思,我就可以配
合修改。
不過話說回來,它也不是那麼好改的,這時就變成,我會建
議你,在你移目錄時,腳本檔目錄的部份就跟著改掉,或者
就像你上面那樣的變通做法亦可。
還有,腳本檔的讀入你可以想成有做底下這行..
sscanf(str,"%s=%s##",s1,s2);
這是非常直覺的。然後它最終的結果則是
quest_data["s1"] = "s2";
從這裡就可推測若 s2 = "ROOT"child##, 上面就會變成
quest_data["s1"] = ""ROOT"child";
這個在以前用 change 去寫房間檔時也用過, 如..
short: "HIG"大草原"NOR"##
也就是說,你可以把 "ROOT" 當成 #define 變數的引用語
法,我希望大家也都可以這樣子想。
→
12/18 13:38, , 11F
12/18 13:38, 11F
※ 編輯: laechan 來自: 210.61.157.53 (12/18 13:39)
推
12/18 16:33, , 12F
12/18 16:33, 12F
→
12/18 16:55, , 13F
12/18 16:55, 13F
推
12/19 09:59, , 14F
12/19 09:59, 14F
→
12/19 10:00, , 15F
12/19 10:00, 15F
mud_sanc 近期熱門文章
PTT遊戲區 即時熱門文章
29
45