private final Set<PlayerClass> getAvailableSubClasses(L2PcInstance player) { final PlayerRace npcRace = getVillageMasterRace(); final ClassType npcTeachType = getVillageMasterTeachType(); // get player base class final int currentBaseId = player.getBaseClass(); final ClassId baseCID = ClassId.values()[currentBaseId]; // we need 2nd occupation ID final int baseClassId; if (baseCID.level() > 2) baseClassId = baseCID.getParent().ordinal(); else baseClassId = currentBaseId; PlayerClass currClass = PlayerClass.values()[baseClassId]; /** * If the race of your main class is Elf or Dark Elf, you may not select each class as a * subclass to the other class, and you may not select Overlord and Warsmith class as a * subclass. You may not select a similar class as the subclass. The occupations classified as * similar classes are as follows: Treasure Hunter, Plainswalker and Abyss Walker Hawkeye, * Silver Ranger and Phantom Ranger Paladin, Dark Avenger, Temple Knight and Shillien Knight * Warlocks, Elemental Summoner and Phantom Summoner Elder and Shillien Elder Swordsinger and * Bladedancer Sorcerer, Spellsinger and Spellhowler */ Set<PlayerClass> availSubs = currClass.getAvailableSubclasses(player); if (availSubs != null && !availSubs.isEmpty()) { for (PlayerClass availSub : availSubs) { for (SubClass subClass : player.getSubClasses().values()) if (subClass.getClassId() == availSub.ordinal()) { availSubs.remove(PlayerClass.values()[availSub.ordinal()]); } for (Iterator<SubClass> subList = iterSubClasses(player); subList.hasNext(); ) { SubClass prevSubClass = subList.next(); int subClassId = prevSubClass.getClassId(); if (subClassId >= 88) { subClassId = ClassId.values()[subClassId].getParent().getId(); } if (availSub.ordinal() == subClassId || availSub.ordinal() == player.getBaseClass()) { availSubs.remove(PlayerClass.values()[availSub.ordinal()]); } } if (npcRace == PlayerRace.Human || npcRace == PlayerRace.LightElf) { // If the master is human or light elf, ensure that fighter-type // masters only teach fighter classes, and priest-type masters // only teach priest classes etc. if (!availSub.isOfType(npcTeachType)) { availSubs.remove(availSub); } else if (!availSub.isOfRace(PlayerRace.Human) && !availSub.isOfRace(PlayerRace.LightElf)) { availSubs.remove(availSub); } } else { // If the master is not human and not light elf, // then remove any classes not of the same race as the master. if (npcRace != PlayerRace.Human && npcRace != PlayerRace.LightElf && !availSub.isOfRace(npcRace)) { availSubs.remove(availSub); } } } } currClass = null; return availSubs; }
@Override public void onBypassFeedback(L2PcInstance player, String command) { String[] commandStr = command.split(" "); String actualCommand = commandStr[0]; // Get actual command String cmdParams = ""; String cmdParams2 = ""; if (commandStr.length >= 2) { cmdParams = commandStr[1]; } if (commandStr.length >= 3) { cmdParams2 = commandStr[2]; } commandStr = null; if (actualCommand.equalsIgnoreCase("create_clan")) { if (cmdParams.equals("")) return; ClanTable.getInstance().createClan(player, cmdParams); } else if (actualCommand.equalsIgnoreCase("create_academy")) { if (cmdParams.equals("")) return; createSubPledge(player, cmdParams, null, L2Clan.SUBUNIT_ACADEMY, 5); } else if (actualCommand.equalsIgnoreCase("create_royal")) { if (cmdParams.equals("")) return; createSubPledge(player, cmdParams, cmdParams2, L2Clan.SUBUNIT_ROYAL1, 6); } else if (actualCommand.equalsIgnoreCase("create_knight")) { if (cmdParams.equals("")) return; createSubPledge(player, cmdParams, cmdParams2, L2Clan.SUBUNIT_KNIGHT1, 7); } else if (actualCommand.equalsIgnoreCase("assign_subpl_leader")) { if (cmdParams.equals("")) return; assignSubPledgeLeader(player, cmdParams, cmdParams2); } else if (actualCommand.equalsIgnoreCase("create_ally")) { if (cmdParams.equals("")) return; if (!player.isClanLeader()) { player.sendPacket(new SystemMessage(SystemMessageId.ONLY_CLAN_LEADER_CREATE_ALLIANCE)); return; } player.getClan().createAlly(player, cmdParams); } else if (actualCommand.equalsIgnoreCase("dissolve_ally")) { if (!player.isClanLeader()) { player.sendPacket(new SystemMessage(SystemMessageId.FEATURE_ONLY_FOR_ALLIANCE_LEADER)); return; } player.getClan().dissolveAlly(player); } else if (actualCommand.equalsIgnoreCase("dissolve_clan")) { dissolveClan(player, player.getClanId()); } else if (actualCommand.equalsIgnoreCase("change_clan_leader")) { if (cmdParams.equals("")) return; changeClanLeader(player, cmdParams); } else if (actualCommand.equalsIgnoreCase("recover_clan")) { recoverClan(player, player.getClanId()); } else if (actualCommand.equalsIgnoreCase("increase_clan_level")) { if (!player.isClanLeader()) { player.sendPacket(new SystemMessage(SystemMessageId.YOU_ARE_NOT_AUTHORIZED_TO_DO_THAT)); return; } player.getClan().levelUpClan(player); } else if (actualCommand.equalsIgnoreCase("learn_clan_skills")) { showPledgeSkillList(player); } else if (command.startsWith("Subclass")) { int cmdChoice = Integer.parseInt(command.substring(9, 10).trim()); // Subclasses may not be changed while a skill is in use. if (player.isCastingNow() || player.isAllSkillsDisabled()) { player.sendPacket( new SystemMessage(SystemMessageId.SUBCLASS_NO_CHANGE_OR_CREATE_WHILE_SKILL_IN_USE)); return; } if (player.getPet() != null && player.getPet().isSummonInstance) { if (player.getPet().isCastingNow() || player.getPet().isAllSkillsDisabled()) { player.sendPacket( new SystemMessage(SystemMessageId.SUBCLASS_NO_CHANGE_OR_CREATE_WHILE_SKILL_IN_USE)); return; } } if (player.isCursedWeaponEquiped()) { player.sendMessage("You can`t change Subclass while Cursed weapon equiped!"); return; } TextBuilder content = new TextBuilder("<html><body>"); NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); Set<PlayerClass> subsAvailable; int paramOne = 0; int paramTwo = 0; try { int endIndex = command.length(); if (command.length() > 13) { endIndex = 13; paramTwo = Integer.parseInt(command.substring(13).trim()); } paramOne = Integer.parseInt(command.substring(11, endIndex).trim()); } catch (Exception NumberFormatException) { } switch (cmdChoice) { case 1: // Add Subclass - Initial // Avoid giving player an option to add a new sub class, if they have three already. if (player.getTotalSubClasses() == Config.ALT_MAX_SUBCLASS_COUNT) { player.sendMessage("You can now only change one of your current sub classes."); return; } subsAvailable = getAvailableSubClasses(player); if (subsAvailable != null && !subsAvailable.isEmpty()) { content.append("Add Subclass:<br>Which sub class do you wish to add?<br>"); for (PlayerClass subClass : subsAvailable) { content.append( "<a action=\"bypass -h npc_" + getObjectId() + "_Subclass 4 " + subClass.ordinal() + "\" msg=\"1268;" + formatClassForDisplay(subClass) + "\">" + formatClassForDisplay(subClass) + "</a><br>"); } } else { player.sendMessage("There are no sub classes available at this time."); return; } break; case 2: // Change Class - Initial content.append("Change Subclass:<br>"); final int baseClassId = player.getBaseClass(); if (player.getSubClasses().isEmpty()) { content.append( "You can't change sub classes when you don't have a sub class to begin with.<br>" + "<a action=\"bypass -h npc_" + getObjectId() + "_Subclass 1\">Add subclass.</a>"); } else { content.append("Which class would you like to switch to?<br>"); if (baseClassId == player.getActiveClass()) { content.append( CharTemplateTable.getClassNameById(baseClassId) + " <font color=\"LEVEL\">(Base Class)</font><br><br>"); } else { content.append( "<a action=\"bypass -h npc_" + getObjectId() + "_Subclass 5 0\">" + CharTemplateTable.getClassNameById(baseClassId) + "</a> " + "<font color=\"LEVEL\">(Base Class)</font><br><br>"); } for (Iterator<SubClass> subList = iterSubClasses(player); subList.hasNext(); ) { SubClass subClass = subList.next(); int subClassId = subClass.getClassId(); if (subClassId == player.getActiveClass()) { content.append(CharTemplateTable.getClassNameById(subClassId) + "<br>"); } else { content.append( "<a action=\"bypass -h npc_" + getObjectId() + "_Subclass 5 " + subClass.getClassIndex() + "\">" + CharTemplateTable.getClassNameById(subClassId) + "</a><br>"); } } } break; case 3: // Change/Cancel Subclass - Initial content.append( "Change Subclass:<br>Which of the following sub classes would you like to change?<br>"); int classIndex = 1; for (Iterator<SubClass> subList = iterSubClasses(player); subList.hasNext(); ) { SubClass subClass = subList.next(); content.append("Sub-class " + classIndex + "<br1>"); content.append( "<a action=\"bypass -h npc_" + getObjectId() + "_Subclass 6 " + subClass.getClassIndex() + "\">" + CharTemplateTable.getClassNameById(subClass.getClassId()) + "</a><br>"); classIndex++; } content.append( "<br>If you change a sub class, you'll start at level 40 after the 2nd class transfer."); break; case 4: // Add Subclass - Action (Subclass 4 x[x]) boolean allowAddition = true; /* * If the character is less than level 75 on any of their previously chosen * classes then disallow them to change to their most recently added sub-class choice. */ if (player.getLevel() < 75) { player.sendMessage( "You may not add a new sub class before you are level 75 on your previous class."); allowAddition = false; } if (player._event != null) { player.sendMessage("Недоступно в данный момент."); return; } if (Olympiad.getInstance().isRegisteredInComp(player) || player.getOlympiadGameId() > 0) { player.sendPacket( new SystemMessage( SystemMessageId .YOU_HAVE_ALREADY_BEEN_REGISTERED_IN_A_WAITING_LIST_OF_AN_EVENT)); return; } if (allowAddition) { if (!player.getSubClasses().isEmpty()) { for (Iterator<SubClass> subList = iterSubClasses(player); subList.hasNext(); ) { SubClass subClass = subList.next(); if (subClass.getLevel() < 75) { player.sendMessage( "You may not add a new sub class before you are level 75 on your previous sub class."); allowAddition = false; break; } } } } /* * If quest checking is enabled, verify if the character has completed the Mimir's Elixir (Path to Subclass) * and Fate's Whisper (A Grade Weapon) quests by checking for instances of their unique reward items. * * If they both exist, remove both unique items and continue with adding the sub-class. */ if (!Config.ALT_GAME_SUBCLASS_WITHOUT_QUESTS) { QuestState qs = player.getQuestState("235_MimirsElixir"); if (qs == null || !qs.getState().getName().equalsIgnoreCase("Completed")) { player.sendMessage( "You must have completed the Mimir's Elixir quest to continue adding your sub class."); return; } /*qs = player.getQuestState("234_FatesWhisper"); if(qs == null || qs.getState().getName() != "Completed") { player.sendMessage("You must have completed the Fate's Whisper quest to continue adding your sub class."); return; }*/ } ////////////////// \\\\\\\\\\\\\\\\\\ if (allowAddition) { String className = CharTemplateTable.getClassNameById(paramOne); if (!player.addSubClass(paramOne, player.getTotalSubClasses() + 1)) { player.sendMessage("The sub class could not be added."); return; } player.setActiveClass(player.getTotalSubClasses()); content.append( "Add Subclass:<br>The sub class of <font color=\"LEVEL\">" + className + "</font> has been added."); player.sendPacket( new SystemMessage(SystemMessageId.CLASS_TRANSFER)); // Transfer to new class. className = null; } else { html.setFile("data/html/villagemaster/SubClass_Fail.htm"); } break; case 5: // Change Class - Action /* * If the character is less than level 75 on any of their previously chosen * classes then disallow them to change to their most recently added sub-class choice. * fix: in waiting for battle in oly can change sublcass * Note: paramOne = classIndex */ if (Olympiad.getInstance().isRegisteredInComp(player) || player.getOlympiadGameId() > 0 || Olympiad.getInstance().isRegistered(player)) { player.sendPacket( new SystemMessage( SystemMessageId .YOU_HAVE_ALREADY_BEEN_REGISTERED_IN_A_WAITING_LIST_OF_AN_EVENT)); return; } // sub class exploit fix if (!FloodProtector.getInstance() .tryPerformAction(player.getObjectId(), FloodProtector.PROTECTED_SUBCLASS)) { player.sendMessage( "You can change Subclass only every " + Config.PROTECTED_SUBCLASS_C + " Millisecond(s)"); return; } player.setActiveClass(paramOne); content.append( "Change Subclass:<br>Your active sub class is now a <font color=\"LEVEL\">" + CharTemplateTable.getClassNameById(player.getActiveClass()) + "</font>."); player.sendPacket( new SystemMessage( SystemMessageId.SUBCLASS_TRANSFER_COMPLETED)); // Transfer completed. break; case 6: // Change/Cancel Subclass - Choice content.append( "Please choose a sub class to change to. If the one you are looking for is not here, " + "please seek out the appropriate master for that class.<br>" + "<font color=\"LEVEL\">Warning!</font> All classes and skills for this class will be removed.<br><br>"); subsAvailable = getAvailableSubClasses(player); if (subsAvailable != null && !subsAvailable.isEmpty()) { for (PlayerClass subClass : subsAvailable) { content.append( "<a action=\"bypass -h npc_" + getObjectId() + "_Subclass 7 " + paramOne + " " + subClass.ordinal() + "\">" + formatClassForDisplay(subClass) + "</a><br>"); } } else { player.sendMessage("There are no sub classes available at this time."); return; } break; case 7: // Change Subclass - Action // check player skills if (Config.CHECK_SKILLS_ON_ENTER && !Config.ALT_GAME_SKILL_LEARN) { player.checkAllowedSkills(); } /* * Warning: the information about this subclass will be removed from the * subclass list even if false! */ if (!FloodProtector.getInstance() .tryPerformAction(player.getObjectId(), FloodProtector.PROTECTED_SUBCLASS)) { _log.warn("Player " + player.getName() + " has performed a subclass change too fast"); player.sendMessage( "You can change Subclass only every " + Config.PROTECTED_SUBCLASS_C + " Millisecond(s)"); return; } if (player.modifySubClass(paramOne, paramTwo)) { player.setActiveClass(paramOne); content.append( "Change Subclass:<br>Your sub class has been changed to <font color=\"LEVEL\">" + CharTemplateTable.getClassNameById(paramTwo) + "</font>."); player.sendPacket( new SystemMessage(SystemMessageId.ADD_NEW_SUBCLASS)); // Subclass added. // check player skills if (Config.CHECK_SKILLS_ON_ENTER && !Config.ALT_GAME_SKILL_LEARN) { player.checkAllowedSkills(); } } else { /* * This isn't good! modifySubClass() removed subclass from memory * we must update _classIndex! Else IndexOutOfBoundsException can turn * up some place down the line along with other seemingly unrelated * problems. */ player.setActiveClass( 0); // Also updates _classIndex plus switching _classid to baseclass. player.sendMessage( "The sub class could not be added, you have been reverted to your base class."); return; } break; } content.append("</body></html>"); // If the content is greater than for a basic blank page, // then assume no external HTML file was assigned. if (content.length() > 26) { html.setHtml(content.toString()); } player.sendPacket(html); content = null; html = null; subsAvailable = null; } else { // this class dont know any other commands, let forward // the command to the parent class super.onBypassFeedback(player, command); } actualCommand = null; cmdParams = null; cmdParams2 = null; }