package scripting; import client.MapleClient; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.locks.Lock; import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptException; import server.quest.MapleQuest; import tools.FileoutputUtil; public class NPCScriptManager extends AbstractScriptManager { private final Map<MapleClient, NPCConversationManager> cms = new WeakHashMap<>(); private static final NPCScriptManager instance = new NPCScriptManager(); public static final NPCScriptManager getInstance() { return instance; } public final void start(final MapleClient c, final int npc) { start(c, npc, null); } public final void start(final MapleClient c, final int npc, String script) { final Lock lock = c.getNPCLock(); lock.lock(); try { if (c.getPlayer().isShowInfo()) { c.getPlayer().showInfo("NPC腳本", false, "開啟對話,NPC:" + npc + " 特殊腳本:" + script + c.getPlayer().getMap()); } if (!cms.containsKey(c) && c.canClickNPC()) { Invocable iv; if (script == null) { iv = getInvocable("NPC/" + npc + ".js", c, true); //safe disposal } else { iv = getInvocable("特殊/" + script + ".js", c, true); //safe disposal } if (iv == null) { if (c.getPlayer().isShowErr()) { c.getPlayer().showInfo("NPC腳本", true, "找不到NPCID:" + npc + " 特殊腳本:" + script + c.getPlayer().getMap()); } System.out.println("\r\n找不到NPCID:" + npc + " 特殊腳本:" + script + c.getPlayer().getMap() + "\r\n"); iv = getInvocable("特殊/notcoded.js", c, true); //safe disposal if (iv == null) { dispose(c); return; } } final ScriptEngine scriptengine = (ScriptEngine) iv; final NPCConversationManager cm = new NPCConversationManager(c, npc, -1, script, ScriptType.NPC, iv); cms.put(c, cm); scriptengine.put("cm", cm); c.getPlayer().setConversation(1); c.setClickedNPC(); try { iv.invokeFunction("start"); // Temporary until I've removed all of start } catch (NoSuchMethodException nsme) { iv.invokeFunction("action", (byte) 1, (byte) 0, 0); } } else { if (c.getPlayer().isShowErr()) { c.getPlayer().showInfo("NPC腳本", true, "無法執行腳本:已有腳本執行-" + cms.containsKey(c) + " | 允許執行腳本-" + c.canClickNPC()); } c.getPlayer().dropMessage(-1, "你當前已經和1個NPC對話了. 如果不是請輸入 @解卡 指令進行解卡。"); } } catch (final ScriptException | NoSuchMethodException e) { System.err.println("執行NPC腳本出錯 : NPC - " + npc + " 特殊腳本 - " + script + " ." + e); FileoutputUtil.log(FileoutputUtil.ScriptEx_Log, "\r\n\r\n執行NPC腳本出錯, NPC - " + npc + " 特殊腳本 - " + script + " ." + e + "\r\n\r\n"); dispose(c); notice(c, npc, script, ScriptType.NPC); } finally { lock.unlock(); } } public final void action(final MapleClient c, final byte mode, final byte type, final int selection) { if (mode != -1) { final NPCConversationManager cm = cms.get(c); if (cm == null || cm.getLastMsg() > -1) { return; } final Lock lock = c.getNPCLock(); lock.lock(); try { if (cm.pendingDisposal) { dispose(c); } else { c.setClickedNPC(); System.err.println("mode " + mode); System.err.println("type " + type); System.err.println("sel " + selection); cm.getIv().invokeFunction("action", mode, type, selection); } } catch (final ScriptException | NoSuchMethodException e) { String str; switch (cm.getType()) { case NPC: str = "執行NPC腳本出錯 : NPC - " + cm.getNpc() + " 特殊腳本 - " + cm.getScript(); break; case ITEM: str = "執行道具腳本出錯 : NPC - " + cm.getNpc() + " 腳本 - " + cm.getScript(); break; case ON_USER_ENTER: str = "執行地圖onUserEnter腳本出錯 : 腳本 - " + cm.getScript(); break; case ON_FIRST_USER_ENTER: str = "執行地圖onFirstUserEnter腳本出錯 : 腳本 - " + cm.getScript(); break; default: str = "執行腳本出錯 : NPC - " + cm.getNpc() + " 腳本 - " + cm.getScript(); } System.err.println(str + " ." + e); FileoutputUtil.log(FileoutputUtil.ScriptEx_Log, "\r\n\r\n" + str + " ." + e + "\r\n\r\n"); dispose(c); notice(c, cm.getNpc(), cm.getScript(), cm.getType()); } finally { lock.unlock(); } } } public final void startQuest(final MapleClient c, final int npc, final int quest) { if (!MapleQuest.getInstance(quest).canStart(c.getPlayer(), null)) { if (c.getPlayer().isShowErr()) { c.getPlayer().showInfo("任務開始腳本", true, "無法開始任務 NPC:" + npc + " 任務:" + MapleQuest.getInstance(quest)); } return; } final Lock lock = c.getNPCLock(); lock.lock(); try { if (c.getPlayer().isShowInfo()) { c.getPlayer().showInfo("任務開始腳本", false, "腳本 - 開始任務 NPC:" + npc + " 任務:" + MapleQuest.getInstance(quest)); } if (!cms.containsKey(c) && c.canClickNPC()) { final Invocable iv = getInvocable("任務/" + quest + ".js", c, true); if (iv == null) { if (c.getPlayer().isShowErr()) { c.getPlayer().showInfo("任務開始腳本", true, "找不到腳本任務 NPC:" + npc + " 任務:" + MapleQuest.getInstance(quest) + c.getPlayer().getMap()); } dispose(c); return; } final ScriptEngine scriptengine = (ScriptEngine) iv; final NPCConversationManager cm = new NPCConversationManager(c, npc, quest, null, ScriptType.QUEST_START, iv); cms.put(c, cm); scriptengine.put("qm", cm); c.getPlayer().setConversation(1); c.setClickedNPC(); iv.invokeFunction("start", (byte) 1, (byte) 0, 0); // start it off as something } else if (c.getPlayer().isShowErr()) { c.getPlayer().showInfo("任務開始腳本", true, "無法執行腳本:已有腳本執行-" + cms.containsKey(c) + " | 允許執行腳本-" + c.canClickNPC()); } } catch (final ScriptException | NoSuchMethodException e) { System.err.println("執行任務開始腳本出錯 : NPC - " + npc + "任務 - " + MapleQuest.getInstance(quest) + " ." + e); FileoutputUtil.log(FileoutputUtil.ScriptEx_Log, "\r\n\r\n執行任務開始腳本出錯 : NPC - " + npc + "任務 - " + MapleQuest.getInstance(quest) + " ." + e + "\r\n\r\n"); dispose(c); notice(c, npc, String.valueOf(quest), ScriptType.QUEST_START); } finally { lock.unlock(); } } public final void startQuest(final MapleClient c, final byte mode, final byte type, final int selection) { final Lock lock = c.getNPCLock(); final NPCConversationManager cm = cms.get(c); if (cm == null || cm.getLastMsg() > -1) { return; } lock.lock(); try { if (cm.pendingDisposal) { dispose(c); } else { c.setClickedNPC(); cm.getIv().invokeFunction("start", mode, type, selection); } } catch (ScriptException | NoSuchMethodException e) { System.err.println("執行任務開始腳本出錯 : NPC - " + cm.getNpc() + "任務 - " + MapleQuest.getInstance(cm.getQuest()) + " ." + e); FileoutputUtil.log(FileoutputUtil.ScriptEx_Log, "\r\n\r\n執行任務開始腳本出錯 : NPC - " + cm.getNpc() + "任務 - " + MapleQuest.getInstance(cm.getQuest()) + " ." + e + "\r\n\r\n"); dispose(c); notice(c, cm.getNpc(), String.valueOf(cm.getQuest()), cm.getType()); } finally { lock.unlock(); } } public final void endQuest(final MapleClient c, final int npc, final int quest, final boolean customEnd) { if (!customEnd && !MapleQuest.getInstance(quest).canComplete(c.getPlayer(), null)) { if (c.getPlayer().isShowErr()) { c.getPlayer().showInfo("任務完成腳本", true, "無法完成任務 NPC:" + npc + " 任務:" + MapleQuest.getInstance(quest)); } return; } final Lock lock = c.getNPCLock(); lock.lock(); try { if (c.getPlayer().isShowInfo()) { c.getPlayer().showInfo("任務完成腳本", false, "腳本 - 完成任務 NPC:" + npc + " 任務:" + MapleQuest.getInstance(quest)); } if (!cms.containsKey(c) && c.canClickNPC()) { final Invocable iv = getInvocable("任務/" + quest + ".js", c, true); if (iv == null) { if (c.getPlayer().isShowErr()) { c.getPlayer().showInfo("任務完成腳本", true, "找不到腳本任務 NPC:" + npc + " 任務:" + MapleQuest.getInstance(quest) + c.getPlayer().getMap()); } dispose(c); return; } final ScriptEngine scriptengine = (ScriptEngine) iv; final NPCConversationManager cm = new NPCConversationManager(c, npc, quest, null, ScriptType.QUEST_END, iv); cms.put(c, cm); scriptengine.put("qm", cm); c.getPlayer().setConversation(1); c.setClickedNPC(); iv.invokeFunction("end", (byte) 1, (byte) 0, 0); // start it off as something } } catch (ScriptException | NoSuchMethodException e) { System.err.println("執行任務完成腳本出錯 : NPC - " + npc + "任務 - " + MapleQuest.getInstance(quest) + " ." + e); FileoutputUtil.log(FileoutputUtil.ScriptEx_Log, "\r\n\r\n執行任務完成腳本出錯 : NPC - " + npc + "任務 - " + MapleQuest.getInstance(quest) + " ." + e + "\r\n\r\n"); dispose(c); notice(c, npc, String.valueOf(quest), ScriptType.QUEST_END); } finally { lock.unlock(); } } public final void endQuest(final MapleClient c, final byte mode, final byte type, final int selection) { final Lock lock = c.getNPCLock(); final NPCConversationManager cm = cms.get(c); if (cm == null || cm.getLastMsg() > -1) { return; } lock.lock(); try { if (cm.pendingDisposal) { dispose(c); } else { c.setClickedNPC(); cm.getIv().invokeFunction("end", mode, type, selection); } } catch (ScriptException | NoSuchMethodException e) { System.err.println("執行任務完成腳本出錯 : NPC - " + cm.getNpc() + "任務 - " + MapleQuest.getInstance(cm.getQuest()) + " ." + e); FileoutputUtil.log(FileoutputUtil.ScriptEx_Log, "\r\n\r\n執行任務完成腳本出錯 : NPC - " + cm.getNpc() + "任務 - " + MapleQuest.getInstance(cm.getQuest()) + " ." + e + "\r\n\r\n"); dispose(c); notice(c, cm.getNpc(), String.valueOf(cm.getQuest()), cm.getType()); } finally { lock.unlock(); } } public final void startItemScript(final MapleClient c, final int npc, final String script) { final Lock lock = c.getNPCLock(); lock.lock(); try { if (c.getPlayer().isShowInfo()) { c.getPlayer().showInfo("道具腳本", false, "開啟對話,NPC:" + npc + " 道具腳本:" + script); } if (!cms.containsKey(c) && c.canClickNPC()) { final Invocable iv = getInvocable("道具/" + script + ".js", c, true); if (iv == null) { if (c.getPlayer().isShowErr()) { c.getPlayer().dropMessage(5, "找不到道具腳本:" + script); c.getPlayer().showInfo("道具腳本", true, "找不到道具腳本:" + script); } System.out.println("\r\n找不到道具腳本:" + script + "\r\n"); dispose(c); return; } final ScriptEngine scriptengine = (ScriptEngine) iv; final NPCConversationManager cm = new NPCConversationManager(c, npc, -1, script, ScriptType.ITEM, iv); cms.put(c, cm); scriptengine.put("im", cm); c.getPlayer().setConversation(1); c.setClickedNPC(); try { iv.invokeFunction("start"); } catch (NoSuchMethodException nsme) { iv.invokeFunction("action", (byte) 1, (byte) 0, 0); } } else if (c.getPlayer().isShowErr()) { c.getPlayer().showInfo("道具腳本", true, "無法執行腳本:已有腳本執行-" + cms.containsKey(c) + " | 允許執行腳本-" + c.canClickNPC()); } } catch (final ScriptException | NoSuchMethodException e) { System.err.println("執行道具腳本出錯 : NPC - " + npc + " 腳本 - " + script + " ." + e); FileoutputUtil.log(FileoutputUtil.ScriptEx_Log, "\r\n\r\n執行道具腳本出錯, NPC - " + npc + " 腳本 - " + script + " ." + e + "\r\n\r\n"); dispose(c); notice(c, npc, script, ScriptType.ITEM); } finally { lock.unlock(); } } public final void onUserEnter(final MapleClient c, final String script) { final Lock lock = c.getNPCLock(); lock.lock(); try { if (c.getPlayer().isShowInfo()) { c.getPlayer().showInfo("onUserEnter腳本", false, "開始onUserEnter腳本:" + script + c.getPlayer().getMap()); } if (!cms.containsKey(c)) { final Invocable iv = getInvocable("地圖/onUserEnter/" + script + ".js", c, true); if (iv == null) { if (c.getPlayer().isShowErr()) { c.getPlayer().showInfo("onUserEnter腳本", true, "找不到onUserEnter腳本:" + script + c.getPlayer().getMap()); } System.out.println("\r\n找不到onUserEnter腳本:" + script + c.getPlayer().getMap() + "\r\n"); dispose(c); return; } final ScriptEngine scriptengine = (ScriptEngine) iv; final NPCConversationManager cm = new NPCConversationManager(c, 0, -1, script, ScriptType.ON_USER_ENTER, iv); cms.put(c, cm); scriptengine.put("ms", cm); c.getPlayer().setConversation(1); c.setClickedNPC(); try { iv.invokeFunction("start"); } catch (NoSuchMethodException nsme) { iv.invokeFunction("action", (byte) 1, (byte) 0, 0); } } else if (c.getPlayer().isShowErr()) { c.getPlayer().showInfo("onUserEnter腳本", true, "無法執行腳本:已有腳本執行-" + cms.containsKey(c)); } } catch (final ScriptException | NoSuchMethodException e) { System.err.println("執行onUserEnter腳本出錯 : " + script + ". " + e); FileoutputUtil.log(FileoutputUtil.ScriptEx_Log, "執行onUserEnter腳本出錯 : " + script + ". " + e); dispose(c); notice(c, 0, script, ScriptType.ON_USER_ENTER); } finally { lock.unlock(); } } public final void onFirstUserEnter(final MapleClient c, final String script) { final Lock lock = c.getNPCLock(); lock.lock(); try { if (c.getPlayer().isShowInfo()) { c.getPlayer().showInfo("onFirstUserEnter腳本", false, "開始onFirstUserEnter腳本:" + script + c.getPlayer().getMap()); } if (!cms.containsKey(c)) { final Invocable iv = getInvocable("地圖/onFirstUserEnter/" + script + ".js", c, true); if (iv == null) { if (c.getPlayer().isShowErr()) { c.getPlayer().showInfo("onFirstUserEnter腳本", true, "找不到onFirstUserEnter腳本:" + script + c.getPlayer().getMap()); } System.out.println("\r\n找不到onFirstUserEnter腳本:" + script + c.getPlayer().getMap() + "\r\n"); dispose(c); return; } final ScriptEngine scriptengine = (ScriptEngine) iv; final NPCConversationManager cm = new NPCConversationManager(c, 0, -1, script, ScriptType.ON_FIRST_USER_ENTER, iv); cms.put(c, cm); scriptengine.put("ms", cm); c.getPlayer().setConversation(1); c.setClickedNPC(); try { iv.invokeFunction("start"); } catch (NoSuchMethodException nsme) { iv.invokeFunction("action", (byte) 1, (byte) 0, 0); } } else if (c.getPlayer().isShowErr()) { c.getPlayer().showInfo("onFirstUserEnter腳本", true, "無法執行腳本:已有腳本執行-" + cms.containsKey(c)); } } catch (final ScriptException | NoSuchMethodException e) { System.err.println("執行地圖onFirstUserEnter腳本出錯 : " + script + ". " + e); FileoutputUtil.log(FileoutputUtil.ScriptEx_Log, "執行地圖onFirstUserEnter腳本出錯 : " + script + ". " + e); dispose(c); notice(c, 0, script, ScriptType.ON_FIRST_USER_ENTER); } finally { lock.unlock(); } } public final void dispose(final MapleClient c) { final NPCConversationManager npccm = cms.get(c); if (npccm != null) { cms.remove(c); switch (npccm.getType()) { case NPC: c.removeScriptEngine("腳本/NPC/" + npccm.getNpc() + ".js"); c.removeScriptEngine("腳本/特殊/" + npccm.getScript() + ".js"); c.removeScriptEngine("腳本/特殊/notcoded.js"); break; case ITEM: c.removeScriptEngine("腳本/道具/" + npccm.getScript() + ".js"); break; case ON_USER_ENTER: c.removeScriptEngine("腳本/地圖/onUserEnter/" + npccm.getScript() + ".js"); break; case ON_FIRST_USER_ENTER: c.removeScriptEngine("腳本/地圖/onFirstUserEnter/" + npccm.getScript() + ".js"); break; default: c.removeScriptEngine("腳本/任務/" + npccm.getQuest() + ".js"); } } if (c.getPlayer() != null && c.getPlayer().getConversation() == 1) { c.getPlayer().setConversation(0); } } public final NPCConversationManager getCM(final MapleClient c) { return cms.get(c); } private void notice(MapleClient c, int npcId, String script, ScriptType type) { String str; switch (type) { case NPC: str = "NPC腳本出錯-NPC:" + npcId + (script != null ? " 特殊腳本:" + script : ""); break; case ITEM: str = "道具腳本出錯-NPC:" + npcId + " 腳本:" + script; break; case ON_USER_ENTER: str = "onUserEnter腳本出錯-腳本:" + script; break; case ON_FIRST_USER_ENTER: str = "onFirstUserEnter腳本出錯-腳本:" + script; break; default: str = "腳本出錯-NPC:" + npcId + " 腳本:" + script; } c.getPlayer().dropMessage(1, str); } }