這篇使用的是事件副本腳本,不是一般 NPC 對話腳本。NPC 通常負責把玩家送進副本,而這支副本腳本負責管理副本地圖、怪物、時間與玩家狀態。
副本腳本的核心概念是「每一組隊伍建立一個獨立空間」。玩家進入後,系統會複製一張副本地圖、生成指定 Boss,並在時間到、Boss 死亡、玩家死亡或離隊時做對應處理。
一、基礎參數設定
腳本最上方通常會先放可調整的參數。日後要換地圖、換 Boss、改血量或改副本名稱,只需要改這一段。
var eventmapid = 910000001; // BOSS出現地圖
var returnmap = 910000000; // 回到的地圖
var boss = 100100;
var bosshp = 2000000;
var bossmp = 1000000;
var pos = [494, 213];
var eventname = "測試副本";
參數用途
eventmapid 是副本內使用的地圖,returnmap 是離開副本後回去的地圖。boss 是要生成的怪物代碼,bosshp 與 bossmp 則用來覆蓋怪物原本的血量和魔量。
二、建立副本 setup()
setup(partyid) 是副本被開啟時的重要入口。它會用隊伍 ID 組出副本名稱,建立一個新的 Event Instance,並複製副本地圖。
function setup(partyid) {
var instanceName = eventname + partyid;
var eim = em.newInstance(instanceName);
var map = eim.createInstanceMapS(eventmapid);
beginQuest(eim);
eim.startEventTimer(10 * 60 * 1000);
return eim;
}
eim.createInstanceMapS(eventmapid) 代表建立這個副本專用的地圖實體。這樣不同隊伍進入時,不會互相看到對方的怪物與進度。
三、開始副本與生成 Boss
beginQuest(eim) 在這份範例裡只做一件事:確認副本存在後,呼叫 monsterSpawn(eim) 生成 Boss。
function beginQuest(eim) {
if (eim != null) {
monsterSpawn(eim);
}
}
function monsterSpawn(eim) {
var map2 = eim.getMapInstance(0);
map2.resetFully();
var mob = em.getMonster(boss);
var modified = em.newMonsterStats();
modified.setOHp(bosshp);
modified.setOMp(bossmp);
mob.setOverrideStats(modified);
eim.registerMonster(mob);
map2.spawnMonsterOnGroundBelow(mob, new java.awt.Point(pos[0], pos[1]));
}
為什麼要 registerMonster?
eim.registerMonster(mob) 會把這隻怪登記到副本管理器裡。這樣怪物死亡時,副本才知道是否已經清光怪物,並觸發 allMonstersDead(eim)。
四、玩家進場與離場
玩家進入副本時,playerEntry(eim, player) 會把玩家傳送到副本地圖的 0 號傳點。玩家如果離開副本地圖,changedMap 會取消玩家在副本中的登記。
function playerEntry(eim, player) {
var map = eim.getMapInstance(0);
player.changeMap(map, map.getPortal(0));
eim.setProperty("isSquadPlayerID_" + player.getId(), "1");
}
function changedMap(eim, player, mapid) {
if (mapid != eventmapid) {
eim.unregisterPlayer(player);
eim.disposeIfPlayerBelow(0, 0);
}
}
disposeIfPlayerBelow(0, 0) 的意思是副本內玩家人數低於或等於指定條件時,清理副本。這能避免玩家都離開後,副本實體還一直留在伺服器裡。
五、副本結束條件
這份範例有兩個主要結束方式:時間到,或副本內怪物全數死亡。
function scheduledTimeout(eim) {
if (eim == null) {
return;
} else {
eim.disposeIfPlayerBelow(100, returnmap);
}
}
function allMonstersDead(eim) {
eim.disposeIfPlayerBelow(100, returnmap);
eim.broadcastPlayerMsg(5, "恭喜挑戰通過。");
}
scheduledTimeout 會在 startEventTimer 設定的時間結束後觸發。範例中設定為 10 * 60 * 1000,也就是 10 分鐘。
六、常見事件函數整理
playerDead
玩家死亡時觸發。注意:如果玩家身上有原地復活,可能不會走這個函數。
playerRevive
玩家復活時觸發。範例中會補 50 HP,並把玩家傳回離場地圖。
playerDisconnected
玩家斷線時觸發。會取消玩家副本登記,並設定回到地圖。
leftParty
玩家離開隊伍時觸發。範例會把玩家踢回返回地圖。
disbandParty
隊伍解散時觸發。通常會直接結束整個副本。
monsterValue
擊殺怪物時觸發,可用來做擊殺計數、階段推進或額外判斷。
七、可延伸功能
範例裡有幾行被註解掉的功能,之後可以依副本需求打開使用:
// map.toggleDrops();
// 拿掉註解後,地圖不會掉落任何原本會掉的東西
// map.spawnNpc(9209101, new java.awt.Point(x, y));
// 在副本地圖上召喚 NPC
// map.addMapDrop(物品代碼, 掉落機率, 最小掉落數量, 最大掉落數量);
// 新增此副本地圖的掉落物
// map.addDrop(怪物代碼, 物品代碼, 掉落機率, 最小掉落數量, 最大掉落數量);
// 新增指定怪物的掉落物
八、完整流程總結
這支副本腳本的流程可以整理成:
setup() 建立副本
→ createInstanceMapS() 複製副本地圖
→ beginQuest() 開始挑戰
→ monsterSpawn() 生成 Boss
→ playerEntry() 玩家進入副本
→ allMonstersDead() 或 scheduledTimeout() 結束副本
→ disposeIfPlayerBelow() 清理副本並送玩家回城
副本篇1先掌握這個最小架構就好:建立副本、生成怪物、處理玩家進出、處理結束條件。後續如果要做多階段 Boss、通關獎勵、限制每日次數、排名紀錄,就可以從這個架構往上加。