這篇是副本入口 NPC 教學,負責「能不能進副本」的判斷;上一篇副本篇1則是副本內部事件腳本,負責「進去後怎麼跑」。
副本入口腳本通常放在 NPC 上。玩家點 NPC 後,腳本會檢查是否有隊伍、是否為隊長、隊伍人數、等級限制、需求道具、每日挑戰次數,全部通過後才呼叫事件管理器開啟副本。
一、入口參數設定
入口腳本最上方會先設定副本限制。這些值會直接影響玩家是否能進場。
load("nashorn:mozilla_compat.js");
var times = 2; // 次數
var minPeople = 1; // 最小人數
var maxPeople = 6; // 最大人數
var minlevel = 120; // 最小等級
var maxlevel = 200; // 最大等級
var needItem = [4031172, 1]; // 需求物品
var party = true; // 是否隊伍
var log = "副本次數"; // log名稱
var name = "測試副本"; // 副本腳本名
name 必須對到副本事件腳本
name = "測試副本" 會拿去呼叫 cm.getEventManager(name)。因此這個名稱要和你的事件副本腳本管理名稱一致,不然入口會顯示「找不到副本」。
二、start 與 action 對話流程
入口 NPC 仍然是一般 NPC 對話腳本,所以會有 start() 和 action()。第一頁先詢問玩家是否確認開始挑戰。
function start() {
status = -1;
action(1, 0, 0);
}
function action(mode, type, selection) {
if (mode == 1) {
status++;
} else if (mode == 0) {
status--;
} else {
cm.dispose();
return;
}
if (status == 0) {
var message = "#w";
message += " #r是否確認開始遠征挑戰?#n\r\n";
cm.sendYesNo(message);
}
}
玩家按下確認後會進入 status == 1,這時才開始真正檢查資格。
三、判斷隊伍或玩家資格
範例用 party 變數決定要走隊伍檢查或單人檢查。
var msg = party ? checkParty(false) : checkPlayer(false);
if (cm.getPlayer().getParty() == null) {
cm.sendOk("您沒有隊伍");
cm.dispose();
return;
}
範例內有呼叫 checkPlayer(false),但這份檔案沒有提供 checkPlayer() 函數。所以目前請維持 party = true 使用隊伍模式;如果要做單人副本,需要另外補上單人檢查函數。
四、取得 EventManager 並檢查副本是否有人
所有條件通過後,入口會嘗試取得副本事件管理器。如果找不到,代表事件腳本名稱、載入位置或服務端註冊有問題。
var em = cm.getEventManager(name);
if (em == null) {
cm.sendOk("找不到副本!");
cm.dispose();
return;
}
var eim_ = em.getInstance(name);
if (eim_ != null && eim_.getPlayerCount() <= 0) {
eim_.disposeIfPlayerBelowCheck(0, 0);
}
eim_ = em.getInstance(name);
這裡會先抓目前是否已有同名副本實體。如果副本裡已經沒人,會先清理舊副本;如果還有人,就不讓新的隊伍進入。
五、開啟副本
當沒有舊副本佔用時,入口會再次檢查隊伍,寫入挑戰次數,最後呼叫 em.startInstance() 把隊伍送進副本。
if (eim_ == null) {
checkParty(true);
cm.setPartyAcLog(log);
em.startInstance(cm.getParty(), cm.getMap());
cm.dispose();
} else {
cm.sendOk("裡面有人了!");
cm.dispose();
}
cm.setPartyAcLog(log)
這行會替隊伍成員寫入挑戰紀錄。後面 ccPlayer.getAcLog(log) >= times 會用這個紀錄判斷隊員是否已達挑戰次數。
六、checkParty 的檢查項目
checkParty(goin) 是入口腳本最重要的函數。它會逐一檢查隊伍是否符合副本要求。
檢查副本是否存在
透過 cm.getEventManager(name) 找副本事件管理器。
檢查是否已有副本
如果同名副本還有人,回傳「裡面有人了」。
檢查隊伍與隊長
沒有隊伍或不是隊長,都不能開啟副本。
檢查人數
隊伍人數必須介於 minPeople 和 maxPeople。
檢查同地圖
隊員必須和隊長在同一張地圖,避免有人不在入口也被送進去。
檢查道具
如果 needItem[0] > 0,每位隊員都要有指定道具數量。
檢查等級
隊員等級不得低於 minlevel,也不得高於 maxlevel。
檢查挑戰次數
使用 getAcLog(log) 判斷是否已達每日或活動次數上限。
七、隊伍成員迴圈
腳本透過隊伍 iterator 逐一抓出每位隊員,再用目前地圖的角色 ID 找到真正在線且同地圖的角色物件。
var party = cm.getPlayer().getParty().getMembers();
var it = party.iterator();
while (it.hasNext()) {
var cPlayer = it.next();
var ccPlayer = cm.getPlayer().getMap().getCharacterById(cPlayer.getId());
if (ccPlayer == null) {
return "你的隊友跟你不同地圖";
}
if (needItem[0] > 0) {
if (!ccPlayer.haveItem(needItem[0], needItem[1])) {
return "您的隊員 " + ccPlayer.getName() + " 沒有 #v" + needItem[0] + "##t" + needItem[0] + "# x " + needItem[1];
}
}
}
這種寫法可以避免只檢查隊伍名單,卻沒有確認隊員是否真的在 NPC 所在地圖。副本入口通常一定要做這道檢查。
八、等級、次數與頻道紀錄
第二次迴圈主要檢查等級與挑戰次數,最後把玩家當前頻道寫進角色設定。
if (ccPlayer.getLevel() > maxlevel) {
return "您的隊員 " + ccPlayer.getName() + " 等級大於 " + maxlevel + " 等哦!";
}
if (ccPlayer.getLevel() < minlevel) {
return "您的隊員 " + ccPlayer.getName() + " 等級小於 " + minlevel + " 等哦!";
}
if (ccPlayer.getAcLog(log) >= times) {
return "您的隊員 " + ccPlayer.getName() + " 挑戰次數已達 " + times + " 次!";
}
ccPlayer.setCharacterSet("副本頻道", ccPlayer.getClient().getChannel(), true);
九、完整入口流程總結
玩家點 NPC
→ 顯示是否開始挑戰
→ checkParty() 檢查隊伍資格
→ cm.getEventManager(name) 取得副本事件
→ em.getInstance(name) 檢查副本是否已有人
→ cm.setPartyAcLog(log) 寫入挑戰次數
→ em.startInstance(cm.getParty(), cm.getMap()) 開啟副本
→ 事件腳本的 setup() 接手建立副本地圖與怪物
入口腳本的責任是把不符合資格的玩家擋在外面,並只在條件完整通過時開啟副本。副本內部怎麼生成怪物、時間到怎麼結束、玩家死亡怎麼處理,則交給事件副本腳本負責。