/* * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If * not, see <http://www.gnu.org/licenses/>. */ package silentium.gameserver.model.actor.instance; import silentium.commons.utils.StringUtil; import silentium.gameserver.configs.NPCConfig; import silentium.gameserver.data.html.StaticHtmPath; import silentium.gameserver.data.xml.CharTemplateData; import silentium.gameserver.model.base.ClassId; import silentium.gameserver.network.SystemMessageId; import silentium.gameserver.network.serverpackets.ActionFailed; import silentium.gameserver.network.serverpackets.MagicSkillUse; import silentium.gameserver.network.serverpackets.NpcHtmlMessage; import silentium.gameserver.network.serverpackets.UserInfo; import silentium.gameserver.tables.ItemTable; import silentium.gameserver.templates.chars.L2NpcTemplate; /** * Custom class allowing you to choose your class. You can customize class rewards as needed items. Check npc.properties for more informations. * This NPC type got 2 differents ways to level : - the normal one, where you have to be at least of the good level NOTE : you have to take 1st * class then 2nd, if you try to take 2nd directly it won't work. - the "allow_entire_tree" version, where you can take class depending of your * current path. NOTE : you don't need to be of the good level. Added to the "change class" function, this NPC can noblesse and give available * skills (related to your current class and level). */ public final class L2ClassMasterInstance extends L2NpcInstance { public L2ClassMasterInstance(int objectId, L2NpcTemplate template) { super(objectId, template); } @Override public void showChatWindow(L2PcInstance player) { player.sendPacket(ActionFailed.STATIC_PACKET); String filename = StaticHtmPath.ClassmasterHtmPath + "disabled.htm"; if (NPCConfig.ALLOW_CLASS_MASTERS) filename = StaticHtmPath.ClassmasterHtmPath + getNpcId() + ".htm"; // Send a Server->Client NpcHtmlMessage containing the text of the L2Npc to the L2PcInstance NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); html.setFile(filename, player); html.replace("%objectId%", String.valueOf(getObjectId())); player.sendPacket(html); } @Override public void onBypassFeedback(L2PcInstance player, String command) { if (!NPCConfig.ALLOW_CLASS_MASTERS) return; if (command.startsWith("1stClass")) showHtmlMenu(player, getObjectId(), 1); else if (command.startsWith("2ndClass")) showHtmlMenu(player, getObjectId(), 2); else if (command.startsWith("3rdClass")) showHtmlMenu(player, getObjectId(), 3); else if (command.startsWith("change_class")) { int val = Integer.parseInt(command.substring(13)); if (checkAndChangeClass(player, val)) { // self-animation player.broadcastPacket(new MagicSkillUse(player, player, 5103, 1, 0, 0)); NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); html.setFile(StaticHtmPath.ClassmasterHtmPath + "ok.htm", player); html.replace("%name%", CharTemplateData.getInstance().getClassNameById(val)); player.sendPacket(html); } } else if (command.startsWith("become_noble")) { NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); if (!player.isNoble()) { player.setNoble(true, true); player.sendPacket(new UserInfo(player)); html.setFile(StaticHtmPath.ClassmasterHtmPath + "nobleok.htm", player); player.sendPacket(html); } else { html.setFile(StaticHtmPath.ClassmasterHtmPath + "alreadynoble.htm", player); player.sendPacket(html); } } else if (command.startsWith("learn_skills")) { player.giveAvailableSkills(); player.sendSkillList(); } else super.onBypassFeedback(player, command); } private static final void showHtmlMenu(L2PcInstance player, int objectId, int level) { NpcHtmlMessage html = new NpcHtmlMessage(objectId); if (!NPCConfig.CLASS_MASTER_SETTINGS.isAllowed(level)) { int jobLevel = player.getClassId().level(); final StringBuilder sb = new StringBuilder(100); sb.append("<html><body>"); switch (jobLevel) { case 0: if (NPCConfig.CLASS_MASTER_SETTINGS.isAllowed(1)) sb.append("Come back here when you reached level 20 to change your class.<br>"); else if (NPCConfig.CLASS_MASTER_SETTINGS.isAllowed(2)) sb.append("Come back after your first occupation change.<br>"); else if (NPCConfig.CLASS_MASTER_SETTINGS.isAllowed(3)) sb.append("Come back after your second occupation change.<br>"); else sb.append("I can't change your occupation.<br>"); break; case 1: if (NPCConfig.CLASS_MASTER_SETTINGS.isAllowed(2)) sb.append("Come back here when you reached level 40 to change your class.<br>"); else if (NPCConfig.CLASS_MASTER_SETTINGS.isAllowed(3)) sb.append("Come back after your second occupation change.<br>"); else sb.append("I can't change your occupation.<br>"); break; case 2: if (NPCConfig.CLASS_MASTER_SETTINGS.isAllowed(3)) sb.append("Come back here when you reached level 76 to change your class.<br>"); else sb.append("I can't change your occupation.<br>"); break; case 3: sb.append("There is no class change available for you anymore.<br>"); break; } sb.append("</body></html>"); html.setHtml(sb.toString()); } else { final ClassId currentClassId = player.getClassId(); if (currentClassId.level() >= level) html.setFile(StaticHtmPath.ClassmasterHtmPath + "nomore.htm", player); else { final int minLevel = getMinLevel(currentClassId.level()); if (player.getLevel() >= minLevel || NPCConfig.ALLOW_ENTIRE_TREE) { final StringBuilder menu = new StringBuilder(100); for (ClassId cid : ClassId.values()) { if (validateClassId(currentClassId, cid) && cid.level() == level) StringUtil.append(menu, "<a action=\"bypass -h npc_%objectId%_change_class ", String.valueOf(cid.getId()), "\">", CharTemplateData.getInstance().getClassNameById(cid.getId()), "</a><br>"); } if (menu.length() > 0) { html.setFile(StaticHtmPath.ClassmasterHtmPath + "template.htm", player); html.replace("%name%", CharTemplateData.getInstance().getClassNameById(currentClassId.getId())); html.replace("%menu%", menu.toString()); } else { html.setFile(StaticHtmPath.ClassmasterHtmPath + "comebacklater.htm", player); html.replace("%level%", String.valueOf(getMinLevel(level - 1))); } } else { if (minLevel < Integer.MAX_VALUE) { html.setFile(StaticHtmPath.ClassmasterHtmPath + "comebacklater.htm", player); html.replace("%level%", String.valueOf(minLevel)); } else html.setFile(StaticHtmPath.ClassmasterHtmPath + "nomore.htm", player); } } } html.replace("%objectId%", String.valueOf(objectId)); html.replace("%req_items%", getRequiredItems(level)); player.sendPacket(html); } private static final boolean checkAndChangeClass(L2PcInstance player, int val) { final ClassId currentClassId = player.getClassId(); if (getMinLevel(currentClassId.level()) > player.getLevel() && !NPCConfig.ALLOW_ENTIRE_TREE) return false; if (!validateClassId(currentClassId, val)) return false; int newJobLevel = currentClassId.level() + 1; // Weight/Inventory check if (!NPCConfig.CLASS_MASTER_SETTINGS.getRewardItems(newJobLevel).isEmpty()) { if (player.getWeightPenalty() >= 3) { player.sendPacket(SystemMessageId.INVENTORY_LESS_THAN_80_PERCENT); return false; } } // check if player have all required items for class transfer for (int _itemId : NPCConfig.CLASS_MASTER_SETTINGS.getRequireItems(newJobLevel).keys()) { int _count = NPCConfig.CLASS_MASTER_SETTINGS.getRequireItems(newJobLevel).get(_itemId); if (player.getInventory().getInventoryItemCount(_itemId, -1) < _count) { player.sendPacket(SystemMessageId.NOT_ENOUGH_ITEMS); return false; } } // get all required items for class transfer for (int _itemId : NPCConfig.CLASS_MASTER_SETTINGS.getRequireItems(newJobLevel).keys()) { int _count = NPCConfig.CLASS_MASTER_SETTINGS.getRequireItems(newJobLevel).get(_itemId); if (!player.destroyItemByItemId("ClassMaster", _itemId, _count, player, true)) return false; } // reward player with items for (int _itemId : NPCConfig.CLASS_MASTER_SETTINGS.getRewardItems(newJobLevel).keys()) { int _count = NPCConfig.CLASS_MASTER_SETTINGS.getRewardItems(newJobLevel).get(_itemId); player.addItem("ClassMaster", _itemId, _count, player, true); } player.setClassId(val); if (player.isSubClassActive()) player.getSubClasses().get(player.getClassIndex()).setClassId(player.getActiveClass()); else player.setBaseClass(player.getActiveClass()); player.broadcastUserInfo(); return true; } /** * @param level * - current skillId level (0 - start, 1 - first, etc) * @return minimum player level required for next class transfer */ private static final int getMinLevel(int level) { switch (level) { case 0: return 20; case 1: return 40; case 2: return 76; default: return Integer.MAX_VALUE; } } /** * Returns true if class change is possible * * @param oldCID * current player ClassId * @param val * new class index * @return */ private static final boolean validateClassId(ClassId oldCID, int val) { try { return validateClassId(oldCID, ClassId.values()[val]); } catch (Exception e) { // possible ArrayOutOfBoundsException } return false; } /** * Returns true if class change is possible * * @param oldCID * current player ClassId * @param newCID * new ClassId * @return true if class change is possible */ private static final boolean validateClassId(ClassId oldCID, ClassId newCID) { if (newCID == null || newCID.getRace() == null) return false; if (oldCID.equals(newCID.getParent())) return true; if (NPCConfig.ALLOW_ENTIRE_TREE && newCID.childOf(oldCID)) return true; return false; } private static String getRequiredItems(int level) { if (NPCConfig.CLASS_MASTER_SETTINGS.getRequireItems(level) == null || NPCConfig.CLASS_MASTER_SETTINGS.getRequireItems(level).isEmpty()) return "<tr><td>none</td></r>"; StringBuilder sb = new StringBuilder(); for (int _itemId : NPCConfig.CLASS_MASTER_SETTINGS.getRequireItems(level).keys()) { int _count = NPCConfig.CLASS_MASTER_SETTINGS.getRequireItems(level).get(_itemId); sb.append("<tr><td><font color=\"LEVEL\">").append(_count).append("</font></td><td>").append(ItemTable.getInstance().getTemplate(_itemId).getName()).append("</td></tr>"); } return sb.toString(); } }