Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- [Java section]
- [SubClassMaster.java]
- /*
- * Copyright (C) 2024 k_gkotzamanidis
- *
- * 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 ai.others.SubClass;
- import java.util.EnumMap;
- import java.util.EnumSet;
- import java.util.Set;
- import java.util.logging.Logger;
- import java.util.stream.Collectors;
- import ai.AbstractNpcAI;
- import gr.l2jmobius.Config;
- import gr.l2jmobius.gameserver.data.xml.CategoryData;
- import gr.l2jmobius.gameserver.data.xml.SkillData;
- import gr.l2jmobius.gameserver.enums.CategoryType;
- import gr.l2jmobius.gameserver.enums.ClassId;
- import gr.l2jmobius.gameserver.enums.Race;
- import gr.l2jmobius.gameserver.enums.SubclassInfoType;
- import gr.l2jmobius.gameserver.model.actor.Npc;
- import gr.l2jmobius.gameserver.model.actor.Player;
- import gr.l2jmobius.gameserver.model.events.EventType;
- import gr.l2jmobius.gameserver.model.events.ListenerRegisterType;
- import gr.l2jmobius.gameserver.model.events.annotations.Id;
- import gr.l2jmobius.gameserver.model.events.annotations.RegisterEvent;
- import gr.l2jmobius.gameserver.model.events.annotations.RegisterType;
- import gr.l2jmobius.gameserver.model.events.impl.creature.npc.OnNpcMenuSelect;
- import gr.l2jmobius.gameserver.model.holders.SubClassHolder;
- import gr.l2jmobius.gameserver.model.olympiad.OlympiadManager;
- import gr.l2jmobius.gameserver.model.skill.Skill;
- import gr.l2jmobius.gameserver.model.zone.ZoneId;
- import gr.l2jmobius.gameserver.network.SystemMessageId;
- import gr.l2jmobius.gameserver.network.serverpackets.AcquireSkillList;
- import gr.l2jmobius.gameserver.network.serverpackets.ExSubjobInfo;
- import gr.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage;
- import gr.l2jmobius.gameserver.network.serverpackets.SystemMessage;
- import gr.l2jmobius.gameserver.network.serverpackets.ability.ExAcquireAPSkillList;
- /**
- * @author k_gkotzamanidis
- */
- public class SubClassMaster extends AbstractNpcAI{
- private static final Logger LOGGER = Logger.getLogger(SubClassMaster.class.getName());
- private static final int ANATOLIUS = 900105;
- private static final int CLIENT_CLASS_BUTTON = 11170000;
- private static final int IDENTITY_CRISIS_SKILL_ID = 1570;
- private static final Set<ClassId> mainSubClassSet;
- private static final Set<ClassId> neverSubClassed = EnumSet.of(ClassId.OVERLORD, ClassId.WARSMITH);
- private static final Set<ClassId> SubClassSet1 = EnumSet.of(ClassId.DARK_AVENGER, ClassId.PALADIN, ClassId.TEMPLE_KNIGHT, ClassId.SHILLIEN_KNIGHT);
- private static final Set<ClassId> SubClassSet2 = EnumSet.of(ClassId.TREASURE_HUNTER, ClassId.ABYSS_WALKER, ClassId.PLAINS_WALKER);
- private static final Set<ClassId> SubClassSet3 = EnumSet.of(ClassId.HAWKEYE, ClassId.SILVER_RANGER, ClassId.PHANTOM_RANGER);
- private static final Set<ClassId> SubClassSet4 = EnumSet.of(ClassId.WARLOCK, ClassId.ELEMENTAL_SUMMONER, ClassId.PHANTOM_SUMMONER);
- private static final Set<ClassId> SubClassSet5 = EnumSet.of(ClassId.SORCERER, ClassId.SPELLSINGER, ClassId.SPELLHOWLER);
- private static final EnumMap<ClassId, Set<ClassId>> SubClassSetMap = new EnumMap<>(ClassId.class);
- static{
- final Set<ClassId> subclasses = CategoryData.getInstance().getCategoryByType(CategoryType.THIRD_CLASS_GROUP).stream().map(ClassId::getClassId).collect(Collectors.toSet());
- subclasses.removeAll(neverSubClassed);
- mainSubClassSet = subclasses;
- SubClassSetMap.put(ClassId.DARK_AVENGER, SubClassSet1);
- SubClassSetMap.put(ClassId.PALADIN, SubClassSet1);
- SubClassSetMap.put(ClassId.TEMPLE_KNIGHT, SubClassSet1);
- SubClassSetMap.put(ClassId.SHILLIEN_KNIGHT, SubClassSet1);
- SubClassSetMap.put(ClassId.TREASURE_HUNTER, SubClassSet2);
- SubClassSetMap.put(ClassId.ABYSS_WALKER, SubClassSet2);
- SubClassSetMap.put(ClassId.PLAINS_WALKER, SubClassSet2);
- SubClassSetMap.put(ClassId.HAWKEYE, SubClassSet3);
- SubClassSetMap.put(ClassId.SILVER_RANGER, SubClassSet3);
- SubClassSetMap.put(ClassId.PHANTOM_RANGER, SubClassSet3);
- SubClassSetMap.put(ClassId.WARLOCK, SubClassSet4);
- SubClassSetMap.put(ClassId.ELEMENTAL_SUMMONER, SubClassSet4);
- SubClassSetMap.put(ClassId.PHANTOM_SUMMONER, SubClassSet4);
- SubClassSetMap.put(ClassId.SORCERER, SubClassSet5);
- SubClassSetMap.put(ClassId.SPELLSINGER, SubClassSet5);
- SubClassSetMap.put(ClassId.SPELLHOWLER, SubClassSet5);
- }
- public static void main(String[] args){
- new SubClassMaster();
- }
- private SubClassMaster(){
- addStartNpc(ANATOLIUS);
- addFirstTalkId(ANATOLIUS);
- addTalkId(ANATOLIUS);
- LOGGER.info("SubClassMaster: The Custom NPC has been Initiallize with ID 900105.");
- }
- @Override
- public String onFirstTalk(Npc npc, Player player){
- return "Welcome.html";
- }
- @Override
- public String onAdvEvent(String event, Npc npc, Player player){
- String htmltext = null;
- switch (event){
- case "main":{
- htmltext = "SubClassMaster.html";
- break;
- }
- case "addSubclass":{
- if (checkRules(npc, player, true)){
- break;
- }else{
- final Set<ClassId> availSubs = getAvailableSubClasses(player);
- final StringBuilder sb = new StringBuilder();
- final NpcHtmlMessage html = getNpcHtmlMessage(player, npc, "SubClassList.html");
- if ((availSubs == null) || availSubs.isEmpty()) {
- break;
- }
- for (ClassId subClass : availSubs){
- if (subClass != null){
- final int classId = subClass.getId();
- final int npcStringId = CLIENT_CLASS_BUTTON + classId;
- sb.append("<fstring p1=\"0\" p2=\"" + classId + "\">" + npcStringId + "</fstring>");
- }
- }
- html.replace("%subclass_list%", sb.toString());
- player.sendPacket(html);
- break;
- }
- }
- case "removeSubclass":{
- if (checkRules(npc, player, false)){
- break;
- }else{
- final StringBuilder sb = new StringBuilder();
- final NpcHtmlMessage html = getNpcHtmlMessage(player, npc, "SubClassList.html");
- for (SubClassHolder subClass : player.getSubClasses().values()) {
- if (subClass != null) {
- final int classId = subClass.getClassId();
- final int npcStringId = CLIENT_CLASS_BUTTON + classId;
- sb.append("<fstring p1=\"2\" p2=\"" + subClass.getClassIndex() + "\">" + npcStringId + "</fstring>");
- }
- }
- html.replace("%subclass_list%", sb.toString());
- player.sendPacket(html);
- break;
- }
- }
- case "changeSubclass":{
- if (checkRules(npc, player, false)){
- break;
- }else{
- final StringBuilder sb = new StringBuilder();
- final NpcHtmlMessage html = getNpcHtmlMessage(player, npc, "SubClassList.html");
- for (SubClassHolder subClass : player.getSubClasses().values()){
- if (subClass != null){
- final int classId = subClass.getClassId();
- final int npcStringId = CLIENT_CLASS_BUTTON + classId;
- if (subClass.getClassIndex() == player.getClassIndex()){
- sb.append("<fstring p1=\"5\" p2=\"" + 0 + "\">" + CLIENT_CLASS_BUTTON + "</fstring>");
- }else{
- sb.append("<fstring p1=\"5\" p2=\"" + subClass.getClassIndex() + "\">" + npcStringId + "</fstring>");
- }
- }
- }
- html.replace("%subclass_list%", sb.toString());
- player.sendPacket(html);
- break;
- }
- }
- }
- return htmltext;
- }
- @RegisterEvent(EventType.ON_NPC_MENU_SELECT)
- @RegisterType(ListenerRegisterType.NPC)
- @Id(ANATOLIUS)
- public void onNpcMenuSelect(OnNpcMenuSelect event){
- final Player player = event.getTalker();
- final Npc npc = event.getNpc();
- final int ask = event.getAsk();
- switch (ask){
- /** Here the confirm button take the SubClass! */
- case 0:{
- final int classId = event.getReply();
- final StringBuilder sb = new StringBuilder();
- final NpcHtmlMessage html = getNpcHtmlMessage(player, npc, "SubClassConfirmAdd.html");
- if (!isValidNewSubClass(player, classId)){
- return;
- }
- final int npcStringId = CLIENT_CLASS_BUTTON + classId;
- sb.append("<fstring p1=\"1\" p2=\"" + classId + "\">" + npcStringId + "</fstring>");
- html.replace("%confirm_add%", sb.toString());
- player.sendPacket(html);
- break;
- }
- /** Here you give the SubClass from Case 0 while press it */
- case 1:{
- final int classId = event.getReply();
- if (!isValidNewSubClass(player, classId)){
- return;
- }
- if (!player.addSubClass(classId, player.getTotalSubClasses() + 1, false)){
- return;
- }
- player.setActiveClass(player.getTotalSubClasses());
- player.sendPacket(new ExSubjobInfo(player, SubclassInfoType.NEW_SLOT_USED));
- final SystemMessage sm = new SystemMessage(SystemMessageId.THE_NEW_DUAL_CLASS_S1_HAS_BEEN_ADDED_CONGRATS);
- sm.addItemName(player.getActiveClass());
- player.sendPacket(sm);
- player.sendPacket(getNpcHtmlMessage(player, npc, "SubClassSuccess.html"));
- final Skill identityCrisis = SkillData.getInstance().getSkill(IDENTITY_CRISIS_SKILL_ID, 1);
- if (identityCrisis != null) {
- identityCrisis.applyEffects(player, player);
- }
- break;
- }
- /** Here take the Subclass index to change with a new! */
- case 2:{
- final int subclassIndex = event.getReply();
- final Set<ClassId> availSubs = getAvailableSubClasses(player);
- final StringBuilder sb = new StringBuilder();
- final NpcHtmlMessage html = getNpcHtmlMessage(player, npc, "SubClassList.html");
- if ((availSubs == null) || availSubs.isEmpty()) {
- return;
- }
- for (ClassId subClass : availSubs){
- if (subClass != null){
- final int classId = subClass.getId();
- final int npcStringId = CLIENT_CLASS_BUTTON + classId;
- sb.append("<fstring p1=\"3\" p2=\"" + classId + "\">" + npcStringId + "</fstring>");
- }
- }
- npc.getVariables().set("SUBCLASS_INDEX_" + player.getObjectId(), subclassIndex);
- html.replace("%subclass_list%", sb.toString());
- player.sendPacket(html);
- break;
- }
- case 3:{
- final int classId = event.getReply();
- final int classIndex = npc.getVariables().getInt("SUBCLASS_INDEX_" + player.getObjectId(), -1);
- if (classIndex < 0){
- return;
- }
- final StringBuilder sb = new StringBuilder();
- final NpcHtmlMessage html = getNpcHtmlMessage(player, npc, "SubClassConfirmRemove.html");
- final int npcStringId = CLIENT_CLASS_BUTTON + classId;
- sb.append("<fstring p1=\"4\" p2=\"" + classId + "\">" + npcStringId + "</fstring>");
- html.replace("%confirm_remove%", sb.toString());
- player.sendPacket(html);
- break;
- }
- case 4:{
- final int classId = event.getReply();
- final int classIndex = npc.getVariables().getInt("SUBCLASS_INDEX_" + player.getObjectId(), -1);
- if (classIndex < 0){
- return;
- }
- if (player.modifySubClass(classIndex, classId, false)){
- player.abortCast();
- player.stopAllEffectsExceptThoseThatLastThroughDeath();
- player.stopAllEffects();
- player.stopCubics();
- player.setActiveClass(classIndex);
- player.sendPacket(new ExSubjobInfo(player, SubclassInfoType.CLASS_CHANGED));
- player.sendPacket(getNpcHtmlMessage(player, npc, "SubClassSuccess.html"));
- final SystemMessage sm = new SystemMessage(SystemMessageId.THE_NEW_DUAL_CLASS_S1_HAS_BEEN_ADDED_CONGRATS);
- sm.addItemName(player.getActiveClass());
- player.sendPacket(sm);
- final Skill identityCrisis = SkillData.getInstance().getSkill(IDENTITY_CRISIS_SKILL_ID, 1);
- if (identityCrisis != null) {
- identityCrisis.applyEffects(player, player);
- }
- }
- break;
- }
- case 5:{
- final int subclassIndex = event.getReply();
- final int activeClass = player.getClassId().getId();
- player.setActiveClass(subclassIndex);
- final SystemMessage msg = new SystemMessage(SystemMessageId.YOU_HAVE_SUCCESSFULLY_SWITCHED_S1_TO_S2);
- msg.addClassId(activeClass);
- msg.addClassId(player.getClassId().getId());
- player.sendPacket(msg);
- player.updateSymbolSealSkills();
- player.broadcastUserInfo();
- player.sendStorageMaxCount();
- player.sendPacket(new AcquireSkillList(player));
- player.sendPacket(new ExSubjobInfo(player, SubclassInfoType.CLASS_CHANGED));
- player.sendPacket(getNpcHtmlMessage(player, npc, "SubClassSuccess.html"));
- player.sendPacket(new ExAcquireAPSkillList(player));
- final Skill identityCrisis = SkillData.getInstance().getSkill(IDENTITY_CRISIS_SKILL_ID, 1);
- if (identityCrisis != null) {
- identityCrisis.applyEffects(player, player);
- }
- break;
- }
- }
- }
- private Set<ClassId> getAvailableSubClasses(Player player) {
- final int currentBaseId = player.getBaseClass();
- final ClassId baseCID = ClassId.getClassId(currentBaseId);
- final int baseClassId = (CategoryData.getInstance().isInCategory(CategoryType.FOURTH_CLASS_GROUP, baseCID.getId()) || CategoryData.getInstance().isInCategory(CategoryType.FIFTH_CLASS_GROUP, baseCID.getId()) || CategoryData.getInstance().isInCategory(CategoryType.SIXTH_CLASS_GROUP, baseCID.getId())) ? baseCID.getParent().getId() : currentBaseId;
- final Set<ClassId> availSubs = getSubclasses(player, baseClassId);
- if ((availSubs != null) && !availSubs.isEmpty()) {
- for (ClassId pclass : availSubs) {
- // scan for already used subclasses
- final int availClassId = pclass.getId();
- final ClassId cid = ClassId.getClassId(availClassId);
- for (SubClassHolder subList : player.getSubClasses().values()) {
- final ClassId subId = ClassId.getClassId(subList.getClassId());
- if (subId.equalsOrChildOf(cid)) {
- availSubs.remove(cid);
- break;
- }
- }
- }
- }
- return availSubs;
- }
- private boolean isValidNewSubClass(Player player, int classId) {
- final ClassId cid = ClassId.getClassId(classId);
- ClassId subClassId;
- for (SubClassHolder subList : player.getSubClasses().values()) {
- subClassId = ClassId.getClassId(subList.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 = (CategoryData.getInstance().isInCategory(CategoryType.FOURTH_CLASS_GROUP, baseCID.getId()) || CategoryData.getInstance().isInCategory(CategoryType.FIFTH_CLASS_GROUP, baseCID.getId()) || CategoryData.getInstance().isInCategory(CategoryType.SIXTH_CLASS_GROUP, baseCID.getId())) ? baseCID.getParent().getId() : currentBaseId;
- final Set<ClassId> availSubs = getSubclasses(player, baseClassId);
- if ((availSubs == null) || availSubs.isEmpty()) {
- return false;
- }
- boolean found = false;
- for (ClassId pclass : availSubs) {
- if (pclass.getId() == classId) {
- found = true;
- break;
- }
- }
- return found;
- }
- public final Set<ClassId> getSubclasses(Player player, int classId) {
- Set<ClassId> subclasses = null;
- final ClassId pClass = ClassId.getClassId(classId);
- if (CategoryData.getInstance().isInCategory(CategoryType.THIRD_CLASS_GROUP, classId) || (CategoryData.getInstance().isInCategory(CategoryType.FOURTH_CLASS_GROUP, classId))) {
- subclasses = EnumSet.copyOf(mainSubClassSet);
- subclasses.remove(pClass);
- // Ertheia classes cannot be subclassed and only Kamael can take Kamael classes as subclasses.
- for (ClassId cid : ClassId.values()) {
- if ((cid.getRace() == Race.ERTHEIA) || ((cid.getRace() == Race.KAMAEL) && (player.getRace() != Race.KAMAEL))) {
- subclasses.remove(cid);
- }
- }
- if (player.getRace() == Race.KAMAEL) {
- if (player.getAppearance().isFemale()) {
- subclasses.remove(ClassId.MALE_SOULBREAKER);
- } else {
- subclasses.remove(ClassId.FEMALE_SOULBREAKER);
- }
- if (!player.getSubClasses().containsKey(2) || (player.getSubClasses().get(2).getLevel() < 75)) {
- subclasses.remove(ClassId.INSPECTOR);
- }
- }
- final Set<ClassId> unavailableClasses = SubClassSetMap.get(pClass);
- if (unavailableClasses != null) {
- subclasses.removeAll(unavailableClasses);
- }
- }
- if (subclasses != null) {
- final ClassId currClassId = player.getClassId();
- for (ClassId tempClass : subclasses) {
- if (currClassId.equalsOrChildOf(tempClass)) {
- subclasses.remove(tempClass);
- }
- }
- }
- return subclasses;
- }
- private NpcHtmlMessage getNpcHtmlMessage(Player player, Npc npc, String fileName) {
- final NpcHtmlMessage html = new NpcHtmlMessage(npc.getObjectId());
- final String text = getHtm(player, fileName);
- if (text == null) {
- LOGGER.info("Cannot find HTML file for " + SubClassMaster.class.getSimpleName() + " AI: " + fileName);
- return null;
- }
- html.setHtml(text);
- return html;
- }
- private boolean checkRules(Npc npc, Player player, boolean addSubclass){
- boolean _check = false;
- final StringBuilder sb = new StringBuilder();
- final NpcHtmlMessage html = getNpcHtmlMessage(player, npc, "SubClassError.html");
- if (addSubclass){
- if (player.getTotalSubClasses() >= Config.MAX_SUBCLASS){
- sb.append("you have reached the limit of Subclasses (" + Config.MAX_SUBCLASS + ").");
- html.replace("%subclass_error%", sb.toString());
- player.sendPacket(html);
- _check = true;
- }
- }
- if (OlympiadManager.getInstance().isRegisteredInComp(player)){
- OlympiadManager.getInstance().unRegisterNoble(player);
- _check = true;
- }
- if (player.isTransformed() || player.isSubclassLocked()){
- sb.append("you are in transformed now.");
- html.replace("%subclass_error%", sb.toString());
- player.sendPacket(html);
- _check = true;
- }else if (player.hasSummon() || player.isMounted() || player.isFlying()){
- sb.append("you have summon a pet or mounted.");
- html.replace("%subclass_error%", sb.toString());
- player.sendPacket(html);
- _check = true;
- }else if (!player.isInventoryUnder90(true) || (player.getWeightPenalty() >= 2)){
- sb.append("your inventory capacity is more 90% or <br> you have weight penalty.");
- html.replace("%subclass_error%", sb.toString());
- player.sendPacket(html);
- _check = true;
- }else if (player.isCastingNow() || player.isAttackingNow() || player.isCombatFlagEquipped() || player.isInCombat() || player.isInDuel()){
- sb.append("you casting spell now or attack.");
- html.replace("%subclass_error%", sb.toString());
- player.sendPacket(html);
- _check = true;
- }else if (!player.isInsideZone(ZoneId.PEACE)){
- sb.append("you are not inside into a Peace Zone.");
- html.replace("%subclass_error%", sb.toString());
- player.sendPacket(html);
- _check = true;
- }else if (player.isDead() || player.isFakeDeath()){
- sb.append("you are dead or use fake Death.");
- html.replace("%subclass_error%", sb.toString());
- player.sendPacket(html);
- _check = true;
- }else if (player.isCursedWeaponEquipped() || player.getReputation() < 0){
- sb.append("you karma of equipped Cursed Weapon.");
- html.replace("%subclass_error%", sb.toString());
- player.sendPacket(html);
- _check = true;
- }else if (player.isAffectedBySkill(IDENTITY_CRISIS_SKILL_ID)){
- sb.append("you are under skill effect wait.");
- html.replace("%subclass_error%", sb.toString());
- player.sendPacket(html);
- _check = true;
- }
- return _check;
- }
- }
- [HTML section]
- [SubClassConfirmAdd.html]
- <html>
- <title>SubClass Master: Confirm Add</title>
- <body>
- <center>
- Here we are the choice is not an easy task!<br>
- <font color="LEVEL">The new subclass will have none of the skill enchants or the skills you learned through codices. Think carefully.</font><br>
- Are you sure you want to go ahead with this decision? <br>
- %confirm_add%
- </center>
- </body>
- </html>
- [SubClassConfirmChange.html]
- <html>
- <title>SubClass Master: Confirm Change</title>
- <body>
- <center>
- %confirm_change%
- </center>
- </body>
- </html>
- [SubClassConfirmRemove.html]
- <html>
- <title>SubClass Master: Confirm Remove</title>
- <body>
- <center>
- %confirm_remove%
- </center>
- </body>
- </html>
- [SubClassError.html]
- <html>
- <title>SubClass Master</title>
- <body>
- <center>
- <img src="L2UI_CT1.HtmlWnd_DF_TextureKnight" width=300 height=220>
- We cannot proceed your request because <font color="LEVEL"> %subclass_error%</font>
- </center>
- </body>
- </html>
- [SubClassList.html]
- <html>
- <title>SubClass Master: Classes List</title>
- <body>
- <center>
- <font color="LEVEL">Select an Class</font><br>
- %subclass_list%
- </center>
- </body>
- </html>
- [SubClassMaster.html]
- <html>
- <title>SubClass Master</title>
- <body>
- <center>
- <button value="Add SubClass" action="bypass -h Quest SubClassMaster addSubclass" width=200 height=21 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF" />
- <button value="Remove SubClass" action="bypass -h Quest SubClassMaster removeSubclass" width=200 height=21 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF" />
- <button value="Change to SubClass" action="bypass -h Quest SubClassMaster changeSubclass" width=200 height=21 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF" />
- </center>
- </body>
- </html>
- [SubClassSuccess.html]
- <html>
- <title>SubClass Master: Success</title>
- <body>
- <table width="295" cellspacing="0">
- <tr>
- <td>
- <center>
- <img src="L2UI_CT1.HtmlWnd_DF_TextureKnight" width=300 height=220>
- <font color="LEVEL">Your subclass was successful,<br> welcome to our new Class</font><br>
- </center>
- </td>
- </tr>
- </table>
- </body>
- </html>
- [Welcome.html]
- <html>
- <title>SubClass Master</title>
- <body>
- <table width="295" cellspacing="0">
- <tr>
- <td>
- <center>
- <img src="L2UI_CT1.HtmlWnd_DF_TextureKnight" width=300 height=220>
- <font color="LEVEL">Welcome.</font><br>
- I am the SubClass Master of this game<br>
- Do you want to switch to another Class?<br>
- <button value="Show me the Path's" action="bypass -h Quest SubClassMaster main" width=200 height=21 back="L2UI_CT1.Button_DF_Down" fore="L2UI_CT1.Button_DF" />
- </center>
- </td>
- </tr>
- </table>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement