Village Master AI scripts adapted from HighFive.

This commit is contained in:
MobiusDev
2017-07-28 12:49:09 +00:00
parent 382ff9b12b
commit bd37dadb39
1239 changed files with 10260 additions and 541 deletions

View File

@ -175,6 +175,7 @@ public final class Config
public static boolean ES_SP_BOOK_NEEDED;
public static boolean DIVINE_SP_BOOK_NEEDED;
public static boolean ALT_GAME_SUBCLASS_WITHOUT_QUESTS;
public static boolean ALT_GAME_SUBCLASS_EVERYWHERE;
public static boolean ALLOW_TRANSFORM_WITHOUT_QUEST;
public static int FEE_DELETE_TRANSFER_SKILLS;
public static int FEE_DELETE_SUBCLASS_SKILLS;
@ -1441,6 +1442,7 @@ public final class Config
ES_SP_BOOK_NEEDED = Character.getBoolean("EnchantSkillSpBookNeeded", true);
DIVINE_SP_BOOK_NEEDED = Character.getBoolean("DivineInspirationSpBookNeeded", true);
ALT_GAME_SUBCLASS_WITHOUT_QUESTS = Character.getBoolean("AltSubClassWithoutQuests", false);
ALT_GAME_SUBCLASS_EVERYWHERE = Character.getBoolean("AltSubclassEverywhere", false);
RESTORE_SERVITOR_ON_RECONNECT = Character.getBoolean("RestoreServitorOnReconnect", true);
RESTORE_PET_ON_RECONNECT = Character.getBoolean("RestorePetOnReconnect", true);
ALLOW_TRANSFORM_WITHOUT_QUEST = Character.getBoolean("AltTransformationWithoutQuest", false);

View File

@ -0,0 +1,44 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 com.l2jmobius.gameserver.model.actor.instance;
import com.l2jmobius.gameserver.enums.Race;
import com.l2jmobius.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jmobius.gameserver.model.base.PlayerClass;
public final class L2VillageMasterDElfInstance extends L2VillageMasterInstance
{
/**
* Creates a village master.
* @param template the village master NPC template
*/
public L2VillageMasterDElfInstance(L2NpcTemplate template)
{
super(template);
}
@Override
protected final boolean checkVillageMasterRace(PlayerClass pclass)
{
if (pclass == null)
{
return false;
}
return pclass.isOfRace(Race.DARK_ELF);
}
}

View File

@ -0,0 +1,44 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 com.l2jmobius.gameserver.model.actor.instance;
import com.l2jmobius.gameserver.enums.Race;
import com.l2jmobius.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jmobius.gameserver.model.base.PlayerClass;
public final class L2VillageMasterDwarfInstance extends L2VillageMasterInstance
{
/**
* Creates a village master.
* @param template the village master NPC template
*/
public L2VillageMasterDwarfInstance(L2NpcTemplate template)
{
super(template);
}
@Override
protected final boolean checkVillageMasterRace(PlayerClass pclass)
{
if (pclass == null)
{
return false;
}
return pclass.isOfRace(Race.DWARF);
}
}

View File

@ -0,0 +1,56 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 com.l2jmobius.gameserver.model.actor.instance;
import com.l2jmobius.gameserver.enums.Race;
import com.l2jmobius.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jmobius.gameserver.model.base.ClassType;
import com.l2jmobius.gameserver.model.base.PlayerClass;
public final class L2VillageMasterFighterInstance extends L2VillageMasterInstance
{
/**
* Creates a village master.
* @param template the village master NPC template
*/
public L2VillageMasterFighterInstance(L2NpcTemplate template)
{
super(template);
}
@Override
protected final boolean checkVillageMasterRace(PlayerClass pclass)
{
if (pclass == null)
{
return false;
}
return pclass.isOfRace(Race.HUMAN) || pclass.isOfRace(Race.ELF);
}
@Override
protected final boolean checkVillageMasterTeachType(PlayerClass pclass)
{
if (pclass == null)
{
return false;
}
return pclass.isOfType(ClassType.Fighter);
}
}

View File

@ -16,15 +16,19 @@
*/
package com.l2jmobius.gameserver.model.actor.instance;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.data.sql.impl.ClanTable;
import com.l2jmobius.gameserver.data.xml.impl.ClassListData;
import com.l2jmobius.gameserver.data.xml.impl.SkillTreesData;
import com.l2jmobius.gameserver.enums.InstanceType;
import com.l2jmobius.gameserver.enums.Race;
import com.l2jmobius.gameserver.instancemanager.CastleManager;
import com.l2jmobius.gameserver.instancemanager.FortManager;
import com.l2jmobius.gameserver.instancemanager.FortSiegeManager;
@ -36,8 +40,12 @@ import com.l2jmobius.gameserver.model.L2SkillLearn;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jmobius.gameserver.model.base.AcquireSkillType;
import com.l2jmobius.gameserver.model.base.ClassId;
import com.l2jmobius.gameserver.model.base.PlayerClass;
import com.l2jmobius.gameserver.model.base.SubClass;
import com.l2jmobius.gameserver.model.entity.Castle;
import com.l2jmobius.gameserver.model.entity.Fort;
import com.l2jmobius.gameserver.model.quest.QuestState;
import com.l2jmobius.gameserver.model.zone.ZoneId;
import com.l2jmobius.gameserver.network.SystemMessageId;
import com.l2jmobius.gameserver.network.serverpackets.ActionFailed;
@ -57,6 +65,10 @@ public class L2VillageMasterInstance extends L2NpcInstance
{
private static Logger _log = Logger.getLogger(L2VillageMasterInstance.class.getName());
/**
* Creates a village master.
* @param template the village master NPC template
*/
public L2VillageMasterInstance(L2NpcTemplate template)
{
super(template);
@ -291,12 +303,599 @@ public class L2VillageMasterInstance extends L2NpcInstance
{
showPledgeSkillList(player);
}
else if (command.startsWith("Subclass"))
{
// Subclasses may not be changed while a skill is in use.
if (player.isCastingNow() || player.isAllSkillsDisabled())
{
player.sendPacket(SystemMessageId.SUBCLASSES_MAY_NOT_BE_CREATED_OR_CHANGED_WHILE_A_SKILL_IS_IN_USE);
return;
}
final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
// Subclasses may not be changed while a transformated state.
if (player.getTransformation() != null)
{
html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_NoTransformed.htm");
player.sendPacket(html);
return;
}
// Subclasses may not be changed while a summon is active.
if (player.hasSummon())
{
html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_NoSummon.htm");
player.sendPacket(html);
return;
}
// Subclasses may not be changed while you have exceeded your inventory limit.
if (!player.isInventoryUnder90(true))
{
player.sendPacket(SystemMessageId.A_SUBCLASS_CANNOT_BE_CREATED_OR_CHANGED_BECAUSE_YOU_HAVE_EXCEEDED_YOUR_INVENTORY_LIMIT);
return;
}
// Subclasses may not be changed while a you are over your weight limit.
if (player.getWeightPenalty() >= 2)
{
player.sendPacket(SystemMessageId.A_SUBCLASS_CANNOT_BE_CREATED_OR_CHANGED_WHILE_YOU_ARE_OVER_YOUR_WEIGHT_LIMIT);
return;
}
int cmdChoice = 0;
int paramOne = 0;
int paramTwo = 0;
try
{
cmdChoice = Integer.parseInt(command.substring(9, 10).trim());
int endIndex = command.indexOf(' ', 11);
if (endIndex == -1)
{
endIndex = command.length();
}
if (command.length() > 11)
{
paramOne = Integer.parseInt(command.substring(11, endIndex).trim());
if (command.length() > endIndex)
{
paramTwo = Integer.parseInt(command.substring(endIndex).trim());
}
}
}
catch (Exception NumberFormatException)
{
_log.warning(L2VillageMasterInstance.class.getName() + ": Wrong numeric values for command " + command);
}
Set<PlayerClass> subsAvailable = null;
switch (cmdChoice)
{
case 0: // Subclass change menu
html.setFile(player.getHtmlPrefix(), getSubClassMenu(player.getRace()));
break;
case 1: // Add Subclass - Initial
// Avoid giving player an option to add a new sub class, if they have max sub-classes already.
if (player.getTotalSubClasses() >= Config.MAX_SUBCLASS)
{
html.setFile(player.getHtmlPrefix(), getSubClassFail());
break;
}
subsAvailable = getAvailableSubClasses(player);
if ((subsAvailable != null) && !subsAvailable.isEmpty())
{
html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_Add.htm");
final StringBuilder content1 = new StringBuilder(200);
for (PlayerClass subClass : subsAvailable)
{
content1.append("<a action=\"bypass -h npc_%objectId%_Subclass 4 " + String.valueOf(subClass.ordinal()) + "\" msg=\"1268;" + ClassListData.getInstance().getClass(subClass.ordinal()).getClassName() + "\">" + ClassListData.getInstance().getClass(subClass.ordinal()).getClientCode() + "</a><br>");
}
html.replace("%list%", content1.toString());
}
else
{
if ((player.getRace() == Race.ELF) || (player.getRace() == Race.DARK_ELF))
{
html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_Fail_Elves.htm");
player.sendPacket(html);
}
else if (player.getRace() == Race.KAMAEL)
{
html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_Fail_Kamael.htm");
player.sendPacket(html);
}
else
{
// TODO: Retail message
player.sendMessage("There are no sub classes available at this time.");
}
return;
}
break;
case 2: // Change Class - Initial
if (player.getSubClasses().isEmpty())
{
html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_ChangeNo.htm");
}
else
{
final StringBuilder content2 = new StringBuilder(200);
if (checkVillageMaster(player.getBaseClass()))
{
content2.append("<a action=\"bypass -h npc_%objectId%_Subclass 5 0\">" + ClassListData.getInstance().getClass(player.getBaseClass()).getClientCode() + "</a><br>");
}
for (Iterator<SubClass> subList = iterSubClasses(player); subList.hasNext();)
{
final SubClass subClass = subList.next();
if (checkVillageMaster(subClass.getClassDefinition()))
{
content2.append("<a action=\"bypass -h npc_%objectId%_Subclass 5 " + String.valueOf(subClass.getClassIndex()) + "\">" + ClassListData.getInstance().getClass(subClass.getClassId()).getClientCode() + "</a><br>");
}
}
if (content2.length() > 0)
{
html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_Change.htm");
html.replace("%list%", content2.toString());
}
else
{
html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_ChangeNotFound.htm");
}
}
break;
case 3: // Change/Cancel Subclass - Initial
if ((player.getSubClasses() == null) || player.getSubClasses().isEmpty())
{
html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_ModifyEmpty.htm");
break;
}
// custom value
if (player.getTotalSubClasses() > 3)
{
html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_ModifyCustom.htm");
final StringBuilder content3 = new StringBuilder(200);
int classIndex = 1;
for (Iterator<SubClass> subList = iterSubClasses(player); subList.hasNext();)
{
final SubClass subClass = subList.next();
content3.append("Sub-class " + String.valueOf(classIndex++) + "<br><a action=\"bypass -h npc_%objectId%_Subclass 6 " + String.valueOf(subClass.getClassIndex()) + "\">" + ClassListData.getInstance().getClass(subClass.getClassId()).getClientCode() + "</a><br>");
}
html.replace("%list%", content3.toString());
}
else
{
// retail html contain only 3 subclasses
html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_Modify.htm");
if (player.getSubClasses().containsKey(1))
{
html.replace("%sub1%", ClassListData.getInstance().getClass(player.getSubClasses().get(1).getClassId()).getClientCode());
}
else
{
html.replace("<a action=\"bypass -h npc_%objectId%_Subclass 6 1\">%sub1%</a><br>", "");
}
if (player.getSubClasses().containsKey(2))
{
html.replace("%sub2%", ClassListData.getInstance().getClass(player.getSubClasses().get(2).getClassId()).getClientCode());
}
else
{
html.replace("<a action=\"bypass -h npc_%objectId%_Subclass 6 2\">%sub2%</a><br>", "");
}
if (player.getSubClasses().containsKey(3))
{
html.replace("%sub3%", ClassListData.getInstance().getClass(player.getSubClasses().get(3).getClassId()).getClientCode());
}
else
{
html.replace("<a action=\"bypass -h npc_%objectId%_Subclass 6 3\">%sub3%</a><br>", "");
}
}
break;
case 4: // Add Subclass - Action (Subclass 4 x[x])
/**
* 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.getFloodProtectors().getSubclass().tryPerformAction("add subclass"))
{
_log.warning(L2VillageMasterInstance.class.getName() + ": Player " + player.getName() + " has performed a subclass change too fast");
return;
}
boolean allowAddition = true;
if (player.getTotalSubClasses() >= Config.MAX_SUBCLASS)
{
allowAddition = false;
}
if (player.getLevel() < 75)
{
allowAddition = false;
}
if (allowAddition)
{
if (!player.getSubClasses().isEmpty())
{
for (Iterator<SubClass> subList = iterSubClasses(player); subList.hasNext();)
{
final SubClass subClass = subList.next();
if (subClass.getLevel() < 75)
{
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 (allowAddition && !Config.ALT_GAME_SUBCLASS_WITHOUT_QUESTS)
{
allowAddition = checkQuests(player);
}
if (allowAddition && isValidNewSubClass(player, paramOne))
{
if (!player.addSubClass(paramOne, player.getTotalSubClasses() + 1, false))
{
return;
}
player.setActiveClass(player.getTotalSubClasses());
html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_AddOk.htm");
player.sendPacket(SystemMessageId.THE_NEW_SUBCLASS_HAS_BEEN_ADDED); // Subclass added.
}
else
{
html.setFile(player.getHtmlPrefix(), getSubClassFail());
}
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. Note: paramOne = classIndex
*/
if (!player.getFloodProtectors().getSubclass().tryPerformAction("change class"))
{
_log.warning(L2VillageMasterInstance.class.getName() + ": Player " + player.getName() + " has performed a subclass change too fast");
return;
}
if (player.getClassIndex() == paramOne)
{
html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_Current.htm");
break;
}
if (paramOne == 0)
{
if (!checkVillageMaster(player.getBaseClass()))
{
return;
}
}
else
{
try
{
if (!checkVillageMaster(player.getSubClasses().get(paramOne).getClassDefinition()))
{
return;
}
}
catch (NullPointerException e)
{
return;
}
}
player.setActiveClass(paramOne);
// TODO: Retail message YOU_HAVE_SUCCESSFULLY_SWITCHED_S1_TO_S2
player.sendMessage("You have successfully switched to your subclass.");
return;
case 6: // Change/Cancel Subclass - Choice
// validity check
if ((paramOne < 1) || (paramOne > Config.MAX_SUBCLASS))
{
return;
}
subsAvailable = getAvailableSubClasses(player);
// another validity check
if ((subsAvailable == null) || subsAvailable.isEmpty())
{
// TODO: Retail message
player.sendMessage("There are no sub classes available at this time.");
return;
}
final StringBuilder content6 = new StringBuilder(200);
for (PlayerClass subClass : subsAvailable)
{
content6.append("<a action=\"bypass -h npc_%objectId%_Subclass 7 " + String.valueOf(paramOne) + " " + String.valueOf(subClass.ordinal()) + "\" msg=\"1445;\">" + ClassListData.getInstance().getClass(subClass.ordinal()).getClientCode() + "</a><br>");
}
switch (paramOne)
{
case 1:
html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_ModifyChoice1.htm");
break;
case 2:
html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_ModifyChoice2.htm");
break;
case 3:
html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_ModifyChoice3.htm");
break;
default:
html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_ModifyChoice.htm");
}
html.replace("%list%", content6.toString());
break;
case 7: // Change Subclass - Action
/**
* Warning: the information about this subclass will be removed from the subclass list even if false!
*/
if (!player.getFloodProtectors().getSubclass().tryPerformAction("change class"))
{
_log.warning(L2VillageMasterInstance.class.getName() + ": Player " + player.getName() + " has performed a subclass change too fast");
return;
}
if (!isValidNewSubClass(player, paramTwo))
{
return;
}
if (player.modifySubClass(paramOne, paramTwo, false))
{
player.abortCast();
player.stopAllEffectsExceptThoseThatLastThroughDeath(); // all effects from old subclass stopped!
player.stopAllEffectsNotStayOnSubclassChange();
player.stopCubics();
player.setActiveClass(paramOne);
html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_ModifyOk.htm");
html.replace("%name%", ClassListData.getInstance().getClass(paramTwo).getClientCode());
player.sendPacket(SystemMessageId.THE_NEW_SUBCLASS_HAS_BEEN_ADDED); // Subclass added.
}
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;
}
html.replace("%objectId%", String.valueOf(getObjectId()));
player.sendPacket(html);
}
else
{
super.onBypassFeedback(player, command);
}
}
protected String getSubClassMenu(Race race)
{
if (Config.ALT_GAME_SUBCLASS_EVERYWHERE || (race != Race.KAMAEL))
{
return "data/html/villagemaster/SubClass.htm";
}
return "data/html/villagemaster/SubClass_NoOther.htm";
}
protected String getSubClassFail()
{
return "data/html/villagemaster/SubClass_Fail.htm";
}
protected boolean checkQuests(L2PcInstance player)
{
// Noble players can add Sub-Classes without quests
if (player.isNoble())
{
return true;
}
QuestState qs = player.getQuestState("Q00234_FatesWhisper"); // TODO: Not added with Saviors.
if ((qs == null) || !qs.isCompleted())
{
return false;
}
qs = player.getQuestState("Q00235_MimirsElixir"); // TODO: Not added with Saviors.
if ((qs == null) || !qs.isCompleted())
{
return false;
}
return true;
}
/**
* Returns list of available subclasses Base class and already used subclasses removed
* @param player
* @return
*/
private final Set<PlayerClass> getAvailableSubClasses(L2PcInstance player)
{
// get player base class
final int currentBaseId = player.getBaseClass();
final ClassId baseCID = ClassId.getClassId(currentBaseId);
// we need 2nd occupation ID
final int baseClassId;
if (baseCID.level() > 2)
{
baseClassId = baseCID.getParent().ordinal();
}
else
{
baseClassId = currentBaseId;
}
/**
* 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. If the race of your main class is Kamael, you may not subclass any other race If the race of your main class is NOT Kamael, you may not subclass any Kamael class 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 Also, Kamael have a special, hidden 4 subclass, the inspector, which can only be taken if you have already completed the other
* two Kamael subclasses
*/
final Set<PlayerClass> availSubs = PlayerClass.values()[baseClassId].getAvailableSubclasses(player);
if ((availSubs != null) && !availSubs.isEmpty())
{
for (Iterator<PlayerClass> availSub = availSubs.iterator(); availSub.hasNext();)
{
final PlayerClass pclass = availSub.next();
// check for the village master
if (!checkVillageMaster(pclass))
{
availSub.remove();
continue;
}
// scan for already used subclasses
final int availClassId = pclass.ordinal();
final ClassId cid = ClassId.getClassId(availClassId);
SubClass prevSubClass;
ClassId subClassId;
for (Iterator<SubClass> subList = iterSubClasses(player); subList.hasNext();)
{
prevSubClass = subList.next();
subClassId = ClassId.getClassId(prevSubClass.getClassId());
if (subClassId.equalsOrChildOf(cid))
{
availSub.remove();
break;
}
}
}
}
return availSubs;
}
/**
* Check new subclass classId for validity (villagemaster race/type, is not contains in previous subclasses, is contains in allowed subclasses) Base class not added into allowed subclasses.
* @param player
* @param classId
* @return
*/
private final boolean isValidNewSubClass(L2PcInstance player, int classId)
{
if (!checkVillageMaster(classId))
{
return false;
}
final ClassId cid = ClassId.values()[classId];
SubClass sub;
ClassId subClassId;
for (Iterator<SubClass> subList = iterSubClasses(player); subList.hasNext();)
{
sub = subList.next();
subClassId = ClassId.values()[sub.getClassId()];
if (subClassId.equalsOrChildOf(cid))
{
return false;
}
}
// get player base class
final int currentBaseId = player.getBaseClass();
final ClassId baseCID = ClassId.getClassId(currentBaseId);
// we need 2nd occupation ID
final int baseClassId;
if (baseCID.level() > 2)
{
baseClassId = baseCID.getParent().ordinal();
}
else
{
baseClassId = currentBaseId;
}
final Set<PlayerClass> availSubs = PlayerClass.values()[baseClassId].getAvailableSubclasses(player);
if ((availSubs == null) || availSubs.isEmpty())
{
return false;
}
boolean found = false;
for (PlayerClass pclass : availSubs)
{
if (pclass.ordinal() == classId)
{
found = true;
break;
}
}
return found;
}
protected boolean checkVillageMasterRace(PlayerClass pclass)
{
return true;
}
protected boolean checkVillageMasterTeachType(PlayerClass pclass)
{
return true;
}
/**
* Returns true if this classId allowed for master
* @param classId
* @return
*/
public final boolean checkVillageMaster(int classId)
{
return checkVillageMaster(PlayerClass.values()[classId]);
}
/**
* Returns true if this PlayerClass is allowed for master
* @param pclass
* @return
*/
public final boolean checkVillageMaster(PlayerClass pclass)
{
if (Config.ALT_GAME_SUBCLASS_EVERYWHERE)
{
return true;
}
return checkVillageMasterRace(pclass) && checkVillageMasterTeachType(pclass);
}
private static final Iterator<SubClass> iterSubClasses(L2PcInstance player)
{
return player.getSubClasses().values().iterator();
}
private static void dissolveClan(L2PcInstance player, int clanId)
{
if (!player.isClanLeader())

View File

@ -0,0 +1,56 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 com.l2jmobius.gameserver.model.actor.instance;
import com.l2jmobius.gameserver.enums.Race;
import com.l2jmobius.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jmobius.gameserver.model.base.ClassType;
import com.l2jmobius.gameserver.model.base.PlayerClass;
public final class L2VillageMasterMysticInstance extends L2VillageMasterInstance
{
/**
* Creates a village master.
* @param template the village master NPC template
*/
public L2VillageMasterMysticInstance(L2NpcTemplate template)
{
super(template);
}
@Override
protected final boolean checkVillageMasterRace(PlayerClass pclass)
{
if (pclass == null)
{
return false;
}
return pclass.isOfRace(Race.HUMAN) || pclass.isOfRace(Race.ELF);
}
@Override
protected final boolean checkVillageMasterTeachType(PlayerClass pclass)
{
if (pclass == null)
{
return false;
}
return pclass.isOfType(ClassType.Mystic);
}
}

View File

@ -0,0 +1,44 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 com.l2jmobius.gameserver.model.actor.instance;
import com.l2jmobius.gameserver.enums.Race;
import com.l2jmobius.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jmobius.gameserver.model.base.PlayerClass;
public final class L2VillageMasterOrcInstance extends L2VillageMasterInstance
{
/**
* Creates a village master.
* @param template the village master NPC template
*/
public L2VillageMasterOrcInstance(L2NpcTemplate template)
{
super(template);
}
@Override
protected final boolean checkVillageMasterRace(PlayerClass pclass)
{
if (pclass == null)
{
return false;
}
return pclass.isOfRace(Race.ORC);
}
}

View File

@ -0,0 +1,56 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 com.l2jmobius.gameserver.model.actor.instance;
import com.l2jmobius.gameserver.enums.Race;
import com.l2jmobius.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jmobius.gameserver.model.base.ClassType;
import com.l2jmobius.gameserver.model.base.PlayerClass;
public final class L2VillageMasterPriestInstance extends L2VillageMasterInstance
{
/**
* Creates a village master.
* @param template the village master NPC template
*/
public L2VillageMasterPriestInstance(L2NpcTemplate template)
{
super(template);
}
@Override
protected final boolean checkVillageMasterRace(PlayerClass pclass)
{
if (pclass == null)
{
return false;
}
return pclass.isOfRace(Race.HUMAN) || pclass.isOfRace(Race.ELF);
}
@Override
protected final boolean checkVillageMasterTeachType(PlayerClass pclass)
{
if (pclass == null)
{
return false;
}
return pclass.isOfType(ClassType.Priest);
}
}

View File

@ -24,9 +24,13 @@ import static com.l2jmobius.gameserver.model.base.ClassType.Fighter;
import static com.l2jmobius.gameserver.model.base.ClassType.Mystic;
import static com.l2jmobius.gameserver.model.base.ClassType.Priest;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Set;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.enums.Race;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
/**
* @author luisantonioa
@ -246,10 +250,49 @@ public enum PlayerClass
eviscerator(Race.ERTHEIA, Fighter, ClassLevel.AWAKEN),
sayhaSeer(Race.ERTHEIA, Mystic, ClassLevel.AWAKEN);
private static final Set<PlayerClass> mainSubclassSet;
private static final Set<PlayerClass> neverSubclassed = EnumSet.of(Overlord, Warsmith);
private static final Set<PlayerClass> subclasseSet1 = EnumSet.of(DarkAvenger, Paladin, TempleKnight, ShillienKnight);
private static final Set<PlayerClass> subclasseSet2 = EnumSet.of(TreasureHunter, AbyssWalker, Plainswalker);
private static final Set<PlayerClass> subclasseSet3 = EnumSet.of(Hawkeye, SilverRanger, PhantomRanger);
private static final Set<PlayerClass> subclasseSet4 = EnumSet.of(Warlock, ElementalSummoner, PhantomSummoner);
private static final Set<PlayerClass> subclasseSet5 = EnumSet.of(Sorceror, Spellsinger, Spellhowler);
private Race _race;
private ClassLevel _level;
private ClassType _type;
private static final EnumMap<PlayerClass, Set<PlayerClass>> subclassSetMap = new EnumMap<>(PlayerClass.class);
static
{
final Set<PlayerClass> subclasses = getSet(null, THIRD);
subclasses.removeAll(neverSubclassed);
mainSubclassSet = subclasses;
subclassSetMap.put(DarkAvenger, subclasseSet1);
subclassSetMap.put(Paladin, subclasseSet1);
subclassSetMap.put(TempleKnight, subclasseSet1);
subclassSetMap.put(ShillienKnight, subclasseSet1);
subclassSetMap.put(TreasureHunter, subclasseSet2);
subclassSetMap.put(AbyssWalker, subclasseSet2);
subclassSetMap.put(Plainswalker, subclasseSet2);
subclassSetMap.put(Hawkeye, subclasseSet3);
subclassSetMap.put(SilverRanger, subclasseSet3);
subclassSetMap.put(PhantomRanger, subclasseSet3);
subclassSetMap.put(Warlock, subclasseSet4);
subclassSetMap.put(ElementalSummoner, subclasseSet4);
subclassSetMap.put(PhantomSummoner, subclasseSet4);
subclassSetMap.put(Sorceror, subclasseSet5);
subclassSetMap.put(Spellsinger, subclasseSet5);
subclassSetMap.put(Spellhowler, subclasseSet5);
}
private PlayerClass(Race race, ClassType pType, ClassLevel pLevel)
{
_race = race;
@ -257,6 +300,65 @@ public enum PlayerClass
_type = pType;
}
public final Set<PlayerClass> getAvailableSubclasses(L2PcInstance player)
{
Set<PlayerClass> subclasses = null;
if (_level == THIRD)
{
if (player.getRace() != Race.KAMAEL)
{
subclasses = EnumSet.copyOf(mainSubclassSet);
subclasses.remove(this);
switch (player.getRace())
{
case ELF:
subclasses.removeAll(getSet(Race.DARK_ELF, THIRD));
break;
case DARK_ELF:
subclasses.removeAll(getSet(Race.ELF, THIRD));
break;
}
subclasses.removeAll(getSet(Race.KAMAEL, THIRD));
final Set<PlayerClass> unavailableClasses = subclassSetMap.get(this);
if (unavailableClasses != null)
{
subclasses.removeAll(unavailableClasses);
}
}
else
{
subclasses = getSet(Race.KAMAEL, THIRD);
subclasses.remove(this);
// Check sex, male subclasses female and vice versa
// If server owner set MaxSubclass > 3 some kamael's cannot take 4 sub
// So, in that situation we must skip sex check
if (Config.MAX_SUBCLASS <= 3)
{
if (player.getAppearance().getSex())
{
subclasses.removeAll(EnumSet.of(femaleSoulbreaker));
}
else
{
subclasses.removeAll(EnumSet.of(maleSoulbreaker));
}
}
if (!player.getSubClasses().containsKey(2) || (player.getSubClasses().get(2).getLevel() < 75))
{
subclasses.removeAll(EnumSet.of(inspector));
}
}
}
return subclasses;
}
public static EnumSet<PlayerClass> getSet(Race race, ClassLevel level)
{
final EnumSet<PlayerClass> allOf = EnumSet.noneOf(PlayerClass.class);