Classmasters rework.

This commit is contained in:
MobiusDev 2017-08-02 12:48:49 +00:00
parent be341e760c
commit 0530bbeb53
39 changed files with 544 additions and 317 deletions

View File

@ -1,5 +1,5 @@
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../data/xsd/classMaster.xsd"> <list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../data/xsd/classMaster.xsd">
<classMaster classChangeEnabled="false" spawnClassMasters="false" showPopupWindow="false"> <classMaster classChangeEnabled="true" spawnClassMasters="true" showPopupWindow="true">
<classChangeOption name="Free"> <classChangeOption name="Free">
<appliesTo> <appliesTo>
<category>FIRST_CLASS_GROUP</category> <category>FIRST_CLASS_GROUP</category>
@ -18,13 +18,16 @@
<item id="57" count="500000" /> <!-- 500,000 Adena for third class --> <item id="57" count="500000" /> <!-- 500,000 Adena for third class -->
</conditions> </conditions>
</classChangeOption> </classChangeOption>
<classChangeOption name="Pay Ancient Adena"> <!--
You can add alternative payment methods.
<classChangeOption name="Pay Coin of Luck">
<appliesTo> <appliesTo>
<category>THIRD_CLASS_GROUP</category> <category>THIRD_CLASS_GROUP</category>
</appliesTo> </appliesTo>
<conditions> <conditions>
<item id="5575" count="50000" /> <!-- 50,000 Ancient Adena for third class --> <item id="4037" count="100" />
</conditions> </conditions>
</classChangeOption> </classChangeOption>
-->
</classMaster> </classMaster>
</list> </list>

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<list applyTaxes="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../xsd/multisell.xsd">
<npcs>
<npc>31756</npc> <!-- Mr. Cat - Test Server Helper -->
<npc>31757</npc> <!-- Queen of Hearts - Test Server Helper -->
</npcs>
<item>
<ingredient id="57" count="200000" /> <!-- Adena -->
<production id="955" count="1" /> <!-- Scroll: Enchant Weapon (D-grade) -->
</item>
<item>
<ingredient id="57" count="24000" /> <!-- Adena -->
<production id="956" count="1" /> <!-- Scroll: Enchant Armor (D-grade) -->
</item>
<item>
<ingredient id="57" count="440000" /> <!-- Adena -->
<production id="951" count="1" /> <!-- Scroll: Enchant Weapon (C-grade) -->
</item>
<item>
<ingredient id="57" count="60000" /> <!-- Adena -->
<production id="952" count="1" /> <!-- Scroll: Enchant Armor (C-grade) -->
</item>
<item>
<ingredient id="57" count="2000000" /> <!-- Adena -->
<production id="947" count="1" /> <!-- Scroll: Enchant Weapon (B-grade) -->
</item>
<item>
<ingredient id="57" count="320000" /> <!-- Adena -->
<production id="948" count="1" /> <!-- Scroll: Enchant Armor (B-grade) -->
</item>
<item>
<ingredient id="57" count="7200000" /> <!-- Adena -->
<production id="729" count="1" /> <!-- Scroll: Enchant Weapon (A-grade) -->
</item>
<item>
<ingredient id="57" count="960000" /> <!-- Adena -->
<production id="730" count="1" /> <!-- Scroll: Enchant Armor (A-grade) -->
</item>
<item>
<ingredient id="57" count="2600" /> <!-- Adena -->
<production id="1458" count="1" /> <!-- Crystal (D-grade) -->
</item>
<item>
<ingredient id="57" count="12000" /> <!-- Adena -->
<production id="1459" count="1" /> <!-- Crystal (C-grade) -->
</item>
<item>
<ingredient id="57" count="36000" /> <!-- Adena -->
<production id="1460" count="1" /> <!-- Crystal (B-grade) -->
</item>
<item>
<ingredient id="57" count="60000" /> <!-- Adena -->
<production id="1461" count="1" /> <!-- Crystal (A-grade) -->
</item>
<item>
<ingredient id="57" count="1100" /> <!-- Adena -->
<production id="2130" count="1" /> <!-- Gemstone (D-grade) -->
</item>
<item>
<ingredient id="57" count="3300" /> <!-- Adena -->
<production id="2131" count="1" /> <!-- Gemstone (C-grade) -->
</item>
<item>
<ingredient id="57" count="11000" /> <!-- Adena -->
<production id="2132" count="1" /> <!-- Gemstone (B-grade) -->
</item>
<item>
<ingredient id="57" count="120000" /> <!-- Adena -->
<production id="2133" count="1" /> <!-- Gemstone (A-grade) -->
</item>
</list>

View File

@ -0,0 +1,231 @@
<?xml version="1.0" encoding="UTF-8"?>
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../xsd/multisell.xsd">
<npcs>
<npc>31756</npc> <!-- Mr. Cat - Test Server Helper -->
<npc>31757</npc> <!-- Queen of Hearts - Test Server Helper -->
</npcs>
<item>
<!-- Adena -->
<ingredient count="3562000" id="57" />
<!-- Dark Crystal Breastplate -->
<production count="1" id="365" />
</item>
<item>
<!-- Adena -->
<ingredient count="2226000" id="57" />
<!-- Dark Crystal Gaiters -->
<production count="1" id="388" />
</item>
<item>
<!-- Adena -->
<ingredient count="1336000" id="57" />
<!-- Dark Crystal Helmet -->
<production count="1" id="512" />
</item>
<item>
<!-- Adena -->
<ingredient count="935000" id="57" />
<!-- Dark Crystal Shield -->
<production count="1" id="641" />
</item>
<item>
<!-- Adena -->
<ingredient count="2671000" id="57" />
<!-- Dark Crystal Leather Armor -->
<production count="1" id="2385" />
</item>
<item>
<!-- Adena -->
<ingredient count="1670000" id="57" />
<!-- Dark Crystal Leggings -->
<production count="1" id="2389" />
</item>
<item>
<!-- Adena -->
<ingredient count="4341000" id="57" />
<!-- Dark Crystal Robe -->
<production count="1" id="2407" />
</item>
<item>
<!-- Adena -->
<ingredient count="5788000" id="57" />
<!-- Tallum Plate Armor -->
<production count="1" id="2382" />
</item>
<item>
<!-- Adena -->
<ingredient count="1336000" id="57" />
<!-- Tallum Helmet -->
<production count="1" id="547" />
</item>
<item>
<!-- Adena -->
<ingredient count="4341000" id="57" />
<!-- Tallum Leather Armor -->
<production count="1" id="2393" />
</item>
<item>
<!-- Adena -->
<ingredient count="2671000" id="57" />
<!-- Tallum Tunic -->
<production count="1" id="2400" />
</item>
<item>
<!-- Adena -->
<ingredient count="1670000" id="57" />
<!-- Tallum Stockings -->
<production count="1" id="2405" />
</item>
<item>
<!-- Adena -->
<ingredient count="890000" id="57" />
<!-- Sealed Dark Crystal Gloves -->
<production count="1" id="5290" />
</item>
<item>
<!-- Adena -->
<ingredient count="890000" id="57" />
<!-- Sealed Tallum Gloves -->
<production count="1" id="5295" />
</item>
<item>
<!-- Adena -->
<ingredient count="890000" id="57" />
<!-- Sealed Dark Crystal Boots -->
<production count="1" id="5291" />
</item>
<item>
<!-- Adena -->
<ingredient count="890000" id="57" />
<!-- Sealed Tallum Boots -->
<production count="1" id="5296" />
</item>
<item>
<!-- Adena -->
<ingredient count="1340000" id="57" />
<!-- Sealed Phoenix Necklace -->
<production count="1" id="6323" />
</item>
<item>
<!-- Adena -->
<ingredient count="1005000" id="57" />
<!-- Sealed Phoenix Earring -->
<production count="1" id="6324" />
</item>
<item>
<!-- Adena -->
<ingredient count="670000" id="57" />
<!-- Sealed Phoenix Ring -->
<production count="1" id="6325" />
</item>
<item>
<!-- Adena -->
<ingredient count="20741000" id="57" />
<!-- Soul Separator -->
<production count="1" id="236" />
</item>
<item>
<!-- Adena -->
<ingredient count="20741000" id="57" />
<!-- Sword of Miracles -->
<production count="1" id="151" />
</item>
<item>
<!-- Adena -->
<ingredient count="20741000" id="57" />
<!-- Dark Legion's Edge -->
<production count="1" id="2500" />
</item>
<item>
<!-- Adena -->
<ingredient count="20741000" id="57" />
<!-- Dragon Slayer -->
<production count="1" id="81" />
</item>
<item>
<!-- Adena -->
<ingredient count="20741000" id="57" />
<!-- Doom Crusher -->
<production count="1" id="7902" />
</item>
<item>
<!-- Adena -->
<ingredient count="20741000" id="57" />
<!-- Flaming Dragon Skull -->
<production count="1" id="7895" />
</item>
<item>
<!-- Adena -->
<ingredient count="20741000" id="57" />
<!-- Elysian -->
<production count="1" id="164" />
</item>
<item>
<!-- Adena -->
<ingredient count="20741000" id="57" />
<!-- Branch of the Mother Tree -->
<production count="1" id="213" />
</item>
<item>
<!-- Adena -->
<ingredient count="20741000" id="57" />
<!-- Tallum Glaive -->
<production count="1" id="305" />
</item>
<item>
<!-- Adena -->
<ingredient count="20741000" id="57" />
<!-- Dragon Grinder -->
<production count="1" id="270" />
</item>
<item>
<!-- Adena -->
<ingredient count="20741000" id="57" />
<!-- Soul Bow -->
<production count="1" id="289" />
</item>
<item>
<ingredient id="57" count="16" /> <!-- Adena -->
<production id="1344" count="1" /> <!-- Mithril Arrow -->
</item>
<item>
<ingredient id="57" count="14" /> <!-- Adena -->
<production id="1463" count="1" /> <!-- Soulshot (D-grade) -->
</item>
<item>
<ingredient id="57" count="22" /> <!-- Adena -->
<production id="1464" count="1" /> <!-- Soulshot (C-grade) -->
</item>
<item>
<ingredient id="57" count="50" /> <!-- Adena -->
<production id="1465" count="1" /> <!-- Soulshot (B-grade) -->
</item>
<item>
<ingredient id="57" count="80" /> <!-- Adena -->
<production id="1466" count="1" /> <!-- Soulshot (A-grade) -->
</item>
<item>
<ingredient id="57" count="28" /> <!-- Adena -->
<production id="3948" count="1" /> <!-- Blessed Spiritshot (D-grade) -->
</item>
<item>
<ingredient id="57" count="42" /> <!-- Adena -->
<production id="3949" count="1" /> <!-- Blessed Spiritshot (C-grade) -->
</item>
<item>
<ingredient id="57" count="245" /> <!-- Adena -->
<production id="3950" count="1" /> <!-- Blessed Spiritshot (B-grade) -->
</item>
<item>
<ingredient id="57" count="290" /> <!-- Adena -->
<production id="3951" count="1" /> <!-- Blessed Spiritshot (A-grade) -->
</item>
<item>
<ingredient id="57" count="20" /> <!-- Adena -->
<production id="6645" count="1" /> <!-- Beast Soulshot -->
</item>
<item>
<ingredient id="57" count="40" /> <!-- Adena -->
<production id="6646" count="1" /> <!-- Beast Spiritshot -->
</item>
</list>

View File

@ -17,6 +17,7 @@
package ai.others.ClassMaster; package ai.others.ClassMaster;
import java.io.File; import java.io.File;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
@ -29,7 +30,6 @@ import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader; import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.commons.util.Rnd;
import com.l2jmobius.gameserver.data.xml.impl.CategoryData; import com.l2jmobius.gameserver.data.xml.impl.CategoryData;
import com.l2jmobius.gameserver.data.xml.impl.ClassListData; import com.l2jmobius.gameserver.data.xml.impl.ClassListData;
import com.l2jmobius.gameserver.data.xml.impl.SkillData; import com.l2jmobius.gameserver.data.xml.impl.SkillData;
@ -50,7 +50,7 @@ import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerLevel
import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerLogin; import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerLogin;
import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerPressTutorialMark; import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerPressTutorialMark;
import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerProfessionChange; import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerProfessionChange;
import com.l2jmobius.gameserver.model.holders.ItemChanceHolder; import com.l2jmobius.gameserver.model.holders.ItemHolder;
import com.l2jmobius.gameserver.model.spawns.SpawnTemplate; import com.l2jmobius.gameserver.model.spawns.SpawnTemplate;
import com.l2jmobius.gameserver.network.serverpackets.PlaySound; import com.l2jmobius.gameserver.network.serverpackets.PlaySound;
import com.l2jmobius.gameserver.network.serverpackets.TutorialCloseHtml; import com.l2jmobius.gameserver.network.serverpackets.TutorialCloseHtml;
@ -65,11 +65,11 @@ import ai.AbstractNpcAI;
public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
// NPCs // NPCs
private static final int[] CLASS_MASTER = private static final List<Integer> CLASS_MASTERS = new ArrayList<>();
{ {
31756, // Mr. Cat CLASS_MASTERS.add(31756); // Mr. Cat
31757, // Queen of Hearts CLASS_MASTERS.add(31757); // Queen of Hearts
}; }
// Misc // Misc
private boolean _isEnabled; private boolean _isEnabled;
private boolean _spawnClassMasters; private boolean _spawnClassMasters;
@ -80,9 +80,9 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
public ClassMaster() public ClassMaster()
{ {
load(); load();
addStartNpc(CLASS_MASTER); addStartNpc(CLASS_MASTERS);
addTalkId(CLASS_MASTER); addTalkId(CLASS_MASTERS);
addFirstTalkId(CLASS_MASTER); addFirstTalkId(CLASS_MASTERS);
} }
@Override @Override
@ -128,8 +128,8 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
if ("classChangeOption".equals(c.getNodeName())) if ("classChangeOption".equals(c.getNodeName()))
{ {
final List<CategoryType> appliedCategories = new LinkedList<>(); final List<CategoryType> appliedCategories = new LinkedList<>();
final List<ItemChanceHolder> requiredItems = new LinkedList<>(); final List<ItemHolder> requiredItems = new LinkedList<>();
final List<ItemChanceHolder> rewardedItems = new LinkedList<>(); final List<ItemHolder> rewardedItems = new LinkedList<>();
boolean setNoble = false; boolean setNoble = false;
boolean setHero = false; boolean setHero = false;
final String optionName = parseString(attrs, "name", ""); final String optionName = parseString(attrs, "name", "");
@ -163,9 +163,8 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
final int itemId = parseInteger(attrs, "id"); final int itemId = parseInteger(attrs, "id");
final int count = parseInteger(attrs, "count", 1); final int count = parseInteger(attrs, "count", 1);
final int chance = parseInteger(attrs, "chance", 100);
rewardedItems.add(new ItemChanceHolder(itemId, chance, count)); rewardedItems.add(new ItemHolder(itemId, count));
} }
else if ("setNoble".equals(r.getNodeName())) else if ("setNoble".equals(r.getNodeName()))
{ {
@ -186,9 +185,8 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
final int itemId = parseInteger(attrs, "id"); final int itemId = parseInteger(attrs, "id");
final int count = parseInteger(attrs, "count", 1); final int count = parseInteger(attrs, "count", 1);
final int chance = parseInteger(attrs, "chance", 100);
requiredItems.add(new ItemChanceHolder(itemId, chance, count)); requiredItems.add(new ItemHolder(itemId, count));
} }
} }
} }
@ -251,28 +249,13 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
case "buyitems": case "buyitems":
{ {
htmltext = npc.getId() == CLASS_MASTER[0] ? "test_server_helper001a.html" : "test_server_helper001b.html"; htmltext = npc.getId() == CLASS_MASTERS.get(0) ? "test_server_helper001a.html" : "test_server_helper001b.html";
break;
}
case "setnoble":
{
if (player.isNoble())
{
htmltext = "test_server_helper025b.html";
}
else if (player.getLevel() < 75)
{
htmltext = "test_server_helper025a.html";
}
else
{
player.setNoble(true);
player.broadcastUserInfo();
// TODO: SetOneTimeQuestFlag(talker, 10385, 1);
htmltext = "test_server_helper025.html";
}
break; break;
} }
/*
* case "setnoble": { if (player.isNoble()) { htmltext = "test_server_helper025b.html"; } else if (player.getLevel() < 75) { htmltext = "test_server_helper025a.html"; } else { player.setNoble(true); player.broadcastUserInfo(); // TODO: SetOneTimeQuestFlag(talker, 10385, 1); htmltext =
* "test_server_helper025.html"; } break; }
*/
case "firstclass": case "firstclass":
{ {
htmltext = getFirstOccupationChangeHtml(player); htmltext = getFirstOccupationChangeHtml(player);
@ -298,38 +281,21 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
htmltext = "test_server_helper011.html"; htmltext = "test_server_helper011.html";
} }
else if (player.isInCategory(CategoryType.AWAKEN_GROUP)) // else if (player.isInCategory(CategoryType.AWAKEN_GROUP))
{ // {
htmltext = "test_server_helper011a.html"; // htmltext = "test_server_helper011a.html";
} // }
else else
{ {
htmltext = "test_server_helper024.html"; htmltext = "test_server_helper024.html";
} }
break; break;
} }
case "awaken": /*
{ * case "awaken": { if (player.isInCategory(CategoryType.FOURTH_CLASS_GROUP) && (player.getLevel() > 84)) { if (changeToNextClass(player)) { player.sendPacket(new PlaySound("ItemSound.quest_fanfare_2")); player.broadcastUserInfo(); player.store(false); // Save player cause if server
if (player.isInCategory(CategoryType.FOURTH_CLASS_GROUP) && (player.getLevel() > 84)) * crashes before this char is saved, he will lose class and the money payed for class change. htmltext = "test_server_helper021.html"; } } else if (player.isInCategory(CategoryType.AWAKEN_GROUP)) { htmltext = "test_server_helper011a.html"; } else { htmltext =
{ * "test_server_helper011b.html"; } break; }
if (changeToNextClass(player)) */
{
player.sendPacket(new PlaySound("ItemSound.quest_fanfare_2"));
player.broadcastUserInfo();
player.store(false); // Save player cause if server crashes before this char is saved, he will lose class and the money payed for class change.
htmltext = "test_server_helper021.html";
}
}
else if (player.isInCategory(CategoryType.AWAKEN_GROUP))
{
htmltext = "test_server_helper011a.html";
}
else
{
htmltext = "test_server_helper011b.html";
}
break;
}
case "setclass": case "setclass":
{ {
if (!st.hasMoreTokens()) if (!st.hasMoreTokens())
@ -352,10 +318,10 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
canChange = CategoryData.getInstance().isInCategory(CategoryType.FOURTH_CLASS_GROUP, classId); canChange = CategoryData.getInstance().isInCategory(CategoryType.FOURTH_CLASS_GROUP, classId);
} }
else if (player.isInCategory(CategoryType.FOURTH_CLASS_GROUP) && (player.getLevel() >= 85)) // 9 // else if (player.isInCategory(CategoryType.FOURTH_CLASS_GROUP) && (player.getLevel() >= 85)) // 9
{ // {
canChange = CategoryData.getInstance().isInCategory(CategoryType.AWAKEN_GROUP, classId); // 11 // canChange = CategoryData.getInstance().isInCategory(CategoryType.AWAKEN_GROUP, classId); // 11
} // }
if (canChange) if (canChange)
{ {
@ -374,31 +340,22 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
htmltext = htmltext.replace("%options%", getClassChangeOptions(player, classId)); htmltext = htmltext.replace("%options%", getClassChangeOptions(player, classId));
return htmltext; return htmltext;
} }
}
final ClassChangeData data = getClassChangeData(classDataIndex);
if (data == null) final ClassChangeData data = getClassChangeData(classDataIndex);
if ((data != null) && (data.getItemsRequired().size() > 0))
{
for (ItemHolder ri : data.getItemsRequired())
{ {
return null; if (player.getInventory().getInventoryItemCount(ri.getId(), -1) < ri.getCount())
{
player.sendMessage("You do not have enough items.");
return null; // No class change if payment failed.
}
} }
for (ItemHolder ri : data.getItemsRequired())
//@formatter:off
final boolean paid = data.getItemsRequired().stream()
.filter(ich -> ich.getChance() > Rnd.get(100)) // Chance to pay the price
.filter(ih -> player.getInventory().getInventoryItemCount(ih.getId(), -1) >= ih.getCount())
.allMatch(ih -> player.destroyItemByItemId(getClass().getSimpleName(), ih.getId(), ih.getCount(), npc, true));
//@formatter:on
if (paid)
{ {
//@formatter:off player.destroyItemByItemId(getClass().getSimpleName(), ri.getId(), ri.getCount(), npc, true);
data.getItemsRewarded().stream()
.filter(ich -> ich.getChance() > Rnd.get(100)) // Chance to receive the reward
.forEach(ih -> player.addItem(getClass().getSimpleName(), ih.getId(), ih.getCount(), npc, true));
//@formatter:on
}
else
{
return null; // No class change if payment failed.
} }
} }
@ -463,6 +420,14 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
} }
break; break;
} }
case "test_server_helper001.html":
{
if (CLASS_MASTERS.contains(npc.getId()))
{
htmltext = event;
}
break;
}
} }
return htmltext; return htmltext;
@ -555,10 +520,10 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
htmltext = "test_server_helper011.html"; htmltext = "test_server_helper011.html";
} }
else if (player.isInCategory(CategoryType.AWAKEN_GROUP)) // else if (player.isInCategory(CategoryType.AWAKEN_GROUP))
{ // {
htmltext = "test_server_helper011a.html"; // htmltext = "test_server_helper011a.html";
} // }
return htmltext; return htmltext;
} }
@ -737,10 +702,10 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
htmltext = "test_server_helper011.html"; htmltext = "test_server_helper011.html";
} }
else if (player.isInCategory(CategoryType.AWAKEN_GROUP)) // else if (player.isInCategory(CategoryType.AWAKEN_GROUP))
{ // {
htmltext = "test_server_helper011a.html"; // htmltext = "test_server_helper011a.html";
} // }
else else
{ {
htmltext = "test_server_helper029.html"; htmltext = "test_server_helper029.html";
@ -804,11 +769,11 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
//@formatter:off //@formatter:off
if ((player.isInCategory(CategoryType.FIRST_CLASS_GROUP) && (player.getLevel() >= 20)) || if ((player.isInCategory(CategoryType.FIRST_CLASS_GROUP) && (player.getLevel() >= 20)) ||
((player.isInCategory(CategoryType.SECOND_CLASS_GROUP) || player.isInCategory(CategoryType.FIRST_CLASS_GROUP)) && (player.getLevel() >= 40)) || ((player.isInCategory(CategoryType.SECOND_CLASS_GROUP) || player.isInCategory(CategoryType.FIRST_CLASS_GROUP)) && (player.getLevel() >= 40)) ||
(player.isInCategory(CategoryType.THIRD_CLASS_GROUP) && (player.getLevel() >= 76)) || (player.isInCategory(CategoryType.THIRD_CLASS_GROUP) && (player.getLevel() >= 76)) /*||
(player.isInCategory(CategoryType.FOURTH_CLASS_GROUP) && (player.getLevel() >= 85))) (player.isInCategory(CategoryType.FOURTH_CLASS_GROUP) && (player.getLevel() >= 85))*/)
//@formatter:on //@formatter:on
{ {
player.sendPacket(new TutorialShowQuestionMark(1001)); player.sendPacket(new TutorialShowQuestionMark(2, 0)); // mark id was 1001 - used 2 for quest text
} }
} }
@ -818,7 +783,7 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
final L2PcInstance player = event.getActiveChar(); final L2PcInstance player = event.getActiveChar();
if (!_showPopupWindow || (event.getMarkId() != 1001)) if (!_showPopupWindow || (event.getMarkId() != 2)) // mark id was 1001 - used 2 for quest text
{ {
return; return;
} }
@ -836,10 +801,10 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
html = getHtm(player.getHtmlPrefix(), "qm_thirdclass.html"); html = getHtm(player.getHtmlPrefix(), "qm_thirdclass.html");
} }
else if (player.isInCategory(CategoryType.FOURTH_CLASS_GROUP) && (player.getLevel() >= 85)) // 9 // else if (player.isInCategory(CategoryType.FOURTH_CLASS_GROUP) && (player.getLevel() >= 85)) // 9
{ // {
html = getHtm(player.getHtmlPrefix(), "qm_awaken.html"); // html = getHtm(player.getHtmlPrefix(), "qm_awaken.html");
} // }
if (html != null) if (html != null)
{ {
@ -906,14 +871,7 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
option.getItemsRequired().forEach(ih -> option.getItemsRequired().forEach(ih ->
{ {
if (ih.getChance() >= 100) sb.append("<tr><td><font color=\"LEVEL\">" + ih.getCount() + "</font></td><td>" + ItemTable.getInstance().getTemplate(ih.getId()).getName() + "</td><td width=30></td></tr>");
{
sb.append("<tr><td><font color=\"LEVEL\">" + ih.getCount() + "</font></td><td>" + ItemTable.getInstance().getTemplate(ih.getId()).getName() + "</td><td width=30></td></tr>");
}
else
{
sb.append("<tr><td><font color=\"LEVEL\">" + ih.getCount() + "</font></td><td>" + ItemTable.getInstance().getTemplate(ih.getId()).getName() + "</td><td width=30><font color=LEVEL>" + ih.getChance() + "%</font></td></tr>");
}
}); });
} }
sb.append("<tr><td>Rewards:</td></tr>"); sb.append("<tr><td>Rewards:</td></tr>");
@ -938,14 +896,7 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
option.getItemsRewarded().forEach(ih -> option.getItemsRewarded().forEach(ih ->
{ {
if (ih.getChance() >= 100) sb.append("<tr><td><font color=\"LEVEL\">" + ih.getCount() + "</font></td><td>" + ItemTable.getInstance().getTemplate(ih.getId()).getName() + "</td><td width=30></td></tr>");
{
sb.append("<tr><td><font color=\"LEVEL\">" + ih.getCount() + "</font></td><td>" + ItemTable.getInstance().getTemplate(ih.getId()).getName() + "</td><td width=30></td></tr>");
}
else
{
sb.append("<tr><td><font color=\"LEVEL\">" + ih.getCount() + "</font></td><td>" + ItemTable.getInstance().getTemplate(ih.getId()).getName() + "</td><td width=30><font color=LEVEL>" + ih.getChance() + "%</font></td></tr>");
}
}); });
if (option.isRewardNoblesse()) if (option.isRewardNoblesse())
@ -971,8 +922,8 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
private final List<CategoryType> _appliedCategories; private final List<CategoryType> _appliedCategories;
private boolean _rewardNoblesse; private boolean _rewardNoblesse;
private boolean _rewardHero; private boolean _rewardHero;
private List<ItemChanceHolder> _itemsRequired; private List<ItemHolder> _itemsRequired;
private List<ItemChanceHolder> _itemsRewarded; private List<ItemHolder> _itemsRewarded;
public ClassChangeData(String name, List<CategoryType> appliedCategories) public ClassChangeData(String name, List<CategoryType> appliedCategories)
{ {
@ -1026,22 +977,22 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
_rewardHero = rewardHero; _rewardHero = rewardHero;
} }
void setItemsRequired(List<ItemChanceHolder> itemsRequired) void setItemsRequired(List<ItemHolder> itemsRequired)
{ {
_itemsRequired = itemsRequired; _itemsRequired = itemsRequired;
} }
public List<ItemChanceHolder> getItemsRequired() public List<ItemHolder> getItemsRequired()
{ {
return _itemsRequired != null ? _itemsRequired : Collections.emptyList(); return _itemsRequired != null ? _itemsRequired : Collections.emptyList();
} }
void setItemsRewarded(List<ItemChanceHolder> itemsRewarded) void setItemsRewarded(List<ItemHolder> itemsRewarded)
{ {
_itemsRewarded = itemsRewarded; _itemsRewarded = itemsRewarded;
} }
public List<ItemChanceHolder> getItemsRewarded() public List<ItemHolder> getItemsRewarded()
{ {
return _itemsRewarded != null ? _itemsRewarded : Collections.emptyList(); return _itemsRewarded != null ? _itemsRewarded : Collections.emptyList();
} }

View File

@ -1,17 +1,17 @@
<html><body>Test Server Helper:<br> <html><body>Test Server Helper:<br>
How may I help you?<br> How may I help you?<br>
<font color="LEVEL">*About haracters*</font><br> <font color="LEVEL">*About Classes*</font><br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster firstclass">"I want to complete the first class transfer."</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster firstclass">"I want to complete the first class transfer."</Button>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster secondclass">"I want to complete the second class transfer."</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster secondclass">"I want to complete the second class transfer."</Button>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster thirdclass">"I want to complete the third class transfer."</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster thirdclass">"I want to complete the third class transfer."</Button>
<!--<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster awaken">"I'm interested in the 4th class transfer (Awaken!)."</Button>--> <!--<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster awaken">"I'm interested in the 4th class transfer (Awaken!)."</Button>-->
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster setnoble">"I want to become a Noblesse!"</Button> <!--<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster setnoble">"I want to become a Noblesse!"</Button>-->
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster ask=-6&reply=2">"I want to acquire the Exalted status."</Button> <!--<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster ask=-6&reply=2">"I want to acquire the Exalted status."</Button>-->
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster learnskills">"I want to learn all skills up to the third class transfer."</Button><br> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster learnskills">"I want to learn all skills up to the third class transfer."</Button><br>
<font color="LEVEL">*About Items*</font><br> <font color="LEVEL">*About Items*</font><br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Multisell 697">"I need some gear."</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Multisell 697">"I need some gear."</Button>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Multisell 622">"I want to purchase some Enhancers and such."</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Multisell 622">"I want to purchase some Enhancers and such."</Button>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Multisell 644">"I want to buy some Dimensional Items."</Button><br> <!--<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Multisell 644">"I want to buy some Dimensional Items."</Button><br>--><br>
<font color="LEVEL">*About Clans*</font><br> <font color="LEVEL">*About Clans*</font><br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster clanlevel">"Can I raise my clan level?" </Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster clanlevel">"Can I raise my clan level?" </Button>
</body> </body>

View File

@ -1,3 +1,3 @@
<html><body>Test Server Guide:<br> <html><body>Test Server Helper:<br>
You've already attempted the third class transfer! You've already attempted the third class transfer!
</body></html> </body></html>

View File

@ -1,3 +1,3 @@
<html><body>Test Server Guide:<br> <html><body>Test Server Helper:<br>
You have successfully transferred class! You have successfully transferred class!
</body></html> </body></html>

View File

@ -2,5 +2,5 @@
Do you want to raise your clan's level?<br> Do you want to raise your clan's level?<br>
You can raise it <font color="LEVEL">up to level 10</font>, using the test server helper.<br> You can raise it <font color="LEVEL">up to level 10</font>, using the test server helper.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster clanlevelup">"I want to level up."</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster clanlevelup">"I want to level up."</Button>
<Button ALIGN=LEFT ICON="RETURN" action="link test_server_helper001.htm">Back</Button> <Button ALIGN=LEFT ICON="RETURN" action="bypass -h Quest ClassMaster test_server_helper001.html">Back</Button>
</body></html> </body></html>

View File

@ -1,3 +1,3 @@
<html><body>Test Server Guide:<br> <html><body>Test Server Helper:<br>
The second class transfer is only available for characters level 40 or above. The second class transfer is only available for characters level 40 or above.
</body></html> </body></html>

View File

@ -1,4 +1,4 @@
<html><body>Test Server Guide:<br> <html><body>Test Server Helper:<br>
characters level 76 or above who have completed their second occupation change can change occupations for a third time.<br> characters level 76 or above who have completed their second occupation change can change occupations for a third time.<br>
Come back after you've finished your second occupation change and reached level 76. Come back after you've finished your second occupation change and reached level 76.
</body></html> </body></html>

View File

@ -253,7 +253,7 @@ public class Q00255_Tutorial extends Quest
qs.setMemoState(2); qs.setMemoState(2);
playSound(player, "ItemSound.quest_tutorial"); playSound(player, "ItemSound.quest_tutorial");
playTutorialVoice(player, "tutorial_voice_013"); playTutorialVoice(player, "tutorial_voice_013");
player.sendPacket(new TutorialShowQuestionMark(0, 1)); player.sendPacket(new TutorialShowQuestionMark(1, 0));
} }
} }

View File

@ -19,21 +19,18 @@ package com.l2jmobius.gameserver.network.serverpackets;
import com.l2jmobius.commons.network.PacketWriter; import com.l2jmobius.commons.network.PacketWriter;
import com.l2jmobius.gameserver.network.OutgoingPackets; import com.l2jmobius.gameserver.network.OutgoingPackets;
/**
* @author Mobius
*/
public final class TutorialShowQuestionMark implements IClientOutgoingPacket public final class TutorialShowQuestionMark implements IClientOutgoingPacket
{ {
private final int _number1; // cond? private final int _questid; // quest id?
private final int _number2; // quest id? private final int _condition; // cond?
public TutorialShowQuestionMark(int number1) public TutorialShowQuestionMark(int questid, int condition)
{ {
_number1 = number1; _questid = questid;
_number2 = 0; _condition = condition;
}
public TutorialShowQuestionMark(int number1, int number2)
{
_number1 = number1;
_number2 = number2;
} }
@Override @Override
@ -41,8 +38,8 @@ public final class TutorialShowQuestionMark implements IClientOutgoingPacket
{ {
OutgoingPackets.TUTORIAL_SHOW_QUESTION_MARK.writeId(packet); OutgoingPackets.TUTORIAL_SHOW_QUESTION_MARK.writeId(packet);
packet.writeC(_number1); packet.writeC(_condition);
packet.writeD(_number2); packet.writeD(_questid);
return true; return true;
} }
} }

View File

@ -3,6 +3,8 @@ L2J-Mobius Classic 2.0
Client: https://mega.nz/#!0lcGQaaI!tuZUFcQS0gVPjzAIPquznOehLqJq0BR-5rm3BXxn6pg Client: https://mega.nz/#!0lcGQaaI!tuZUFcQS0gVPjzAIPquznOehLqJq0BR-5rm3BXxn6pg
Use mega downloader to download https://megadownloader.en.softonic.com/ Use mega downloader to download https://megadownloader.en.softonic.com/
Mirror: https://drive.google.com/file/d/0B0Pl9EOrFIjuSVNPXzRpRFRZWlk
What is done What is done
-Classic packet compatibility -Classic packet compatibility
-Parsed item data from client -Parsed item data from client
@ -32,6 +34,7 @@ What is done
Custom work Custom work
-Newbie Helper NPC location info -Newbie Helper NPC location info
-Newbie Helper buff support until 40 level -Newbie Helper buff support until 40 level
-ClassMaster (Test Server Helper) NPCs
TODO list TODO list
-Parse retail monster spawns by zone -Parse retail monster spawns by zone
@ -40,7 +43,6 @@ TODO list
-Test quests and update rewards -Test quests and update rewards
-Giran Luxury Shop updates -Giran Luxury Shop updates
-New Race Track arena -New Race Track arena
-Custom ClassMaster NPCs
-Blacksmith NPC updates -Blacksmith NPC updates
-Floran Agricultural Area clan halls -Floran Agricultural Area clan halls
-Test Castle Sieges and Merc stats -Test Castle Sieges and Merc stats

View File

@ -17,6 +17,7 @@
package ai.others.ClassMaster; package ai.others.ClassMaster;
import java.io.File; import java.io.File;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
@ -29,7 +30,6 @@ import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader; import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.commons.util.Rnd;
import com.l2jmobius.gameserver.data.xml.impl.CategoryData; import com.l2jmobius.gameserver.data.xml.impl.CategoryData;
import com.l2jmobius.gameserver.data.xml.impl.ClassListData; import com.l2jmobius.gameserver.data.xml.impl.ClassListData;
import com.l2jmobius.gameserver.data.xml.impl.SkillData; import com.l2jmobius.gameserver.data.xml.impl.SkillData;
@ -50,7 +50,7 @@ import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerLevel
import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerLogin; import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerLogin;
import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerPressTutorialMark; import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerPressTutorialMark;
import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerProfessionChange; import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerProfessionChange;
import com.l2jmobius.gameserver.model.holders.ItemChanceHolder; import com.l2jmobius.gameserver.model.holders.ItemHolder;
import com.l2jmobius.gameserver.model.spawns.SpawnTemplate; import com.l2jmobius.gameserver.model.spawns.SpawnTemplate;
import com.l2jmobius.gameserver.network.serverpackets.PlaySound; import com.l2jmobius.gameserver.network.serverpackets.PlaySound;
import com.l2jmobius.gameserver.network.serverpackets.TutorialCloseHtml; import com.l2jmobius.gameserver.network.serverpackets.TutorialCloseHtml;
@ -65,11 +65,11 @@ import ai.AbstractNpcAI;
public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
// NPCs // NPCs
private static final int[] CLASS_MASTER = private static final List<Integer> CLASS_MASTERS = new ArrayList<>();
{ {
31756, // Mr. Cat CLASS_MASTERS.add(31756); // Mr. Cat
31757, // Queen of Hearts CLASS_MASTERS.add(31757); // Queen of Hearts
}; }
// Misc // Misc
private boolean _isEnabled; private boolean _isEnabled;
private boolean _spawnClassMasters; private boolean _spawnClassMasters;
@ -80,9 +80,9 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
public ClassMaster() public ClassMaster()
{ {
load(); load();
addStartNpc(CLASS_MASTER); addStartNpc(CLASS_MASTERS);
addTalkId(CLASS_MASTER); addTalkId(CLASS_MASTERS);
addFirstTalkId(CLASS_MASTER); addFirstTalkId(CLASS_MASTERS);
} }
@Override @Override
@ -128,8 +128,8 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
if ("classChangeOption".equals(c.getNodeName())) if ("classChangeOption".equals(c.getNodeName()))
{ {
final List<CategoryType> appliedCategories = new LinkedList<>(); final List<CategoryType> appliedCategories = new LinkedList<>();
final List<ItemChanceHolder> requiredItems = new LinkedList<>(); final List<ItemHolder> requiredItems = new LinkedList<>();
final List<ItemChanceHolder> rewardedItems = new LinkedList<>(); final List<ItemHolder> rewardedItems = new LinkedList<>();
boolean setNoble = false; boolean setNoble = false;
boolean setHero = false; boolean setHero = false;
final String optionName = parseString(attrs, "name", ""); final String optionName = parseString(attrs, "name", "");
@ -163,9 +163,8 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
final int itemId = parseInteger(attrs, "id"); final int itemId = parseInteger(attrs, "id");
final int count = parseInteger(attrs, "count", 1); final int count = parseInteger(attrs, "count", 1);
final int chance = parseInteger(attrs, "chance", 100);
rewardedItems.add(new ItemChanceHolder(itemId, chance, count)); rewardedItems.add(new ItemHolder(itemId, count));
} }
else if ("setNoble".equals(r.getNodeName())) else if ("setNoble".equals(r.getNodeName()))
{ {
@ -186,9 +185,8 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
final int itemId = parseInteger(attrs, "id"); final int itemId = parseInteger(attrs, "id");
final int count = parseInteger(attrs, "count", 1); final int count = parseInteger(attrs, "count", 1);
final int chance = parseInteger(attrs, "chance", 100);
requiredItems.add(new ItemChanceHolder(itemId, chance, count)); requiredItems.add(new ItemHolder(itemId, count));
} }
} }
} }
@ -251,7 +249,7 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
case "buyitems": case "buyitems":
{ {
htmltext = npc.getId() == CLASS_MASTER[0] ? "test_server_helper001a.html" : "test_server_helper001b.html"; htmltext = npc.getId() == CLASS_MASTERS.get(0) ? "test_server_helper001a.html" : "test_server_helper001b.html";
break; break;
} }
case "setnoble": case "setnoble":
@ -374,31 +372,22 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
htmltext = htmltext.replace("%options%", getClassChangeOptions(player, classId)); htmltext = htmltext.replace("%options%", getClassChangeOptions(player, classId));
return htmltext; return htmltext;
} }
}
final ClassChangeData data = getClassChangeData(classDataIndex);
if (data == null) final ClassChangeData data = getClassChangeData(classDataIndex);
if ((data != null) && (data.getItemsRequired().size() > 0))
{
for (ItemHolder ri : data.getItemsRequired())
{ {
return null; if (player.getInventory().getInventoryItemCount(ri.getId(), -1) < ri.getCount())
{
player.sendMessage("You do not have enough items.");
return null; // No class change if payment failed.
}
} }
for (ItemHolder ri : data.getItemsRequired())
//@formatter:off
final boolean paid = data.getItemsRequired().stream()
.filter(ich -> ich.getChance() > Rnd.get(100)) // Chance to pay the price
.filter(ih -> player.getInventory().getInventoryItemCount(ih.getId(), -1) >= ih.getCount())
.allMatch(ih -> player.destroyItemByItemId(getClass().getSimpleName(), ih.getId(), ih.getCount(), npc, true));
//@formatter:on
if (paid)
{ {
//@formatter:off player.destroyItemByItemId(getClass().getSimpleName(), ri.getId(), ri.getCount(), npc, true);
data.getItemsRewarded().stream()
.filter(ich -> ich.getChance() > Rnd.get(100)) // Chance to receive the reward
.forEach(ih -> player.addItem(getClass().getSimpleName(), ih.getId(), ih.getCount(), npc, true));
//@formatter:on
}
else
{
return null; // No class change if payment failed.
} }
} }
@ -463,6 +452,14 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
} }
break; break;
} }
case "test_server_helper001.html":
{
if (CLASS_MASTERS.contains(npc.getId()))
{
htmltext = event;
}
break;
}
} }
return htmltext; return htmltext;
@ -808,7 +805,7 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
(player.isInCategory(CategoryType.FOURTH_CLASS_GROUP) && (player.getLevel() >= 85))) (player.isInCategory(CategoryType.FOURTH_CLASS_GROUP) && (player.getLevel() >= 85)))
//@formatter:on //@formatter:on
{ {
player.sendPacket(new TutorialShowQuestionMark(1001)); player.sendPacket(new TutorialShowQuestionMark(1, 0)); // mark id was 1001 - used 1 for tutorial text
} }
} }
@ -818,7 +815,7 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
final L2PcInstance player = event.getActiveChar(); final L2PcInstance player = event.getActiveChar();
if (!_showPopupWindow || (event.getMarkId() != 1001)) if (!_showPopupWindow || (event.getMarkId() != 1)) // mark id was 1001 - used 1 for tutorial text
{ {
return; return;
} }
@ -906,14 +903,7 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
option.getItemsRequired().forEach(ih -> option.getItemsRequired().forEach(ih ->
{ {
if (ih.getChance() >= 100) sb.append("<tr><td><font color=\"LEVEL\">" + ih.getCount() + "</font></td><td>" + ItemTable.getInstance().getTemplate(ih.getId()).getName() + "</td><td width=30></td></tr>");
{
sb.append("<tr><td><font color=\"LEVEL\">" + ih.getCount() + "</font></td><td>" + ItemTable.getInstance().getTemplate(ih.getId()).getName() + "</td><td width=30></td></tr>");
}
else
{
sb.append("<tr><td><font color=\"LEVEL\">" + ih.getCount() + "</font></td><td>" + ItemTable.getInstance().getTemplate(ih.getId()).getName() + "</td><td width=30><font color=LEVEL>" + ih.getChance() + "%</font></td></tr>");
}
}); });
} }
sb.append("<tr><td>Rewards:</td></tr>"); sb.append("<tr><td>Rewards:</td></tr>");
@ -938,14 +928,7 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
option.getItemsRewarded().forEach(ih -> option.getItemsRewarded().forEach(ih ->
{ {
if (ih.getChance() >= 100) sb.append("<tr><td><font color=\"LEVEL\">" + ih.getCount() + "</font></td><td>" + ItemTable.getInstance().getTemplate(ih.getId()).getName() + "</td><td width=30></td></tr>");
{
sb.append("<tr><td><font color=\"LEVEL\">" + ih.getCount() + "</font></td><td>" + ItemTable.getInstance().getTemplate(ih.getId()).getName() + "</td><td width=30></td></tr>");
}
else
{
sb.append("<tr><td><font color=\"LEVEL\">" + ih.getCount() + "</font></td><td>" + ItemTable.getInstance().getTemplate(ih.getId()).getName() + "</td><td width=30><font color=LEVEL>" + ih.getChance() + "%</font></td></tr>");
}
}); });
if (option.isRewardNoblesse()) if (option.isRewardNoblesse())
@ -971,8 +954,8 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
private final List<CategoryType> _appliedCategories; private final List<CategoryType> _appliedCategories;
private boolean _rewardNoblesse; private boolean _rewardNoblesse;
private boolean _rewardHero; private boolean _rewardHero;
private List<ItemChanceHolder> _itemsRequired; private List<ItemHolder> _itemsRequired;
private List<ItemChanceHolder> _itemsRewarded; private List<ItemHolder> _itemsRewarded;
public ClassChangeData(String name, List<CategoryType> appliedCategories) public ClassChangeData(String name, List<CategoryType> appliedCategories)
{ {
@ -1026,22 +1009,22 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
_rewardHero = rewardHero; _rewardHero = rewardHero;
} }
void setItemsRequired(List<ItemChanceHolder> itemsRequired) void setItemsRequired(List<ItemHolder> itemsRequired)
{ {
_itemsRequired = itemsRequired; _itemsRequired = itemsRequired;
} }
public List<ItemChanceHolder> getItemsRequired() public List<ItemHolder> getItemsRequired()
{ {
return _itemsRequired != null ? _itemsRequired : Collections.emptyList(); return _itemsRequired != null ? _itemsRequired : Collections.emptyList();
} }
void setItemsRewarded(List<ItemChanceHolder> itemsRewarded) void setItemsRewarded(List<ItemHolder> itemsRewarded)
{ {
_itemsRewarded = itemsRewarded; _itemsRewarded = itemsRewarded;
} }
public List<ItemChanceHolder> getItemsRewarded() public List<ItemHolder> getItemsRewarded()
{ {
return _itemsRewarded != null ? _itemsRewarded : Collections.emptyList(); return _itemsRewarded != null ? _itemsRewarded : Collections.emptyList();
} }

View File

@ -1,12 +1,12 @@
<html><body>Test Server Helper:<br> <html><body>Test Server Helper:<br>
How may I help you?<br> How may I help you?<br>
<font color="LEVEL">*About haracters*</font><br> <font color="LEVEL">*About characters*</font><br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster firstclass">"I want to complete the first class transfer."</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster firstclass">"I want to complete the first class transfer."</Button>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster secondclass">"I want to complete the second class transfer."</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster secondclass">"I want to complete the second class transfer."</Button>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster thirdclass">"I want to complete the third class transfer."</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster thirdclass">"I want to complete the third class transfer."</Button>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster awaken">"I'm interested in the 4th class transfer (Awaken!)."</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster awaken">"I'm interested in the 4th class transfer (Awaken!)."</Button>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster setnoble">"I want to become a Noblesse!"</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster setnoble">"I want to become a Noblesse!"</Button>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster ask=-6&reply=2">"I want to acquire the Exalted status."</Button> <!--<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster ask=-6&reply=2">"I want to acquire the Exalted status."</Button>-->
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster learnskills">"I want to learn all skills up to the third class transfer."</Button><br> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster learnskills">"I want to learn all skills up to the third class transfer."</Button><br>
<font color="LEVEL">*About Items*</font><br> <font color="LEVEL">*About Items*</font><br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Multisell 697">"I need some gear."</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Multisell 697">"I need some gear."</Button>

View File

@ -1,3 +1,3 @@
<html><body>Test Server Guide:<br> <html><body>Test Server Helper:<br>
You've already attempted the third class transfer! You've already attempted the third class transfer!
</body></html> </body></html>

View File

@ -1,3 +1,3 @@
<html><body>Test Server Guide:<br> <html><body>Test Server Helper:<br>
You have successfully transferred class! You have successfully transferred class!
</body></html> </body></html>

View File

@ -2,5 +2,5 @@
Do you want to raise your clan's level?<br> Do you want to raise your clan's level?<br>
You can raise it <font color="LEVEL">up to level 10</font>, using the test server helper.<br> You can raise it <font color="LEVEL">up to level 10</font>, using the test server helper.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster clanlevelup">"I want to level up."</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster clanlevelup">"I want to level up."</Button>
<Button ALIGN=LEFT ICON="RETURN" action="link test_server_helper001.htm">Back</Button> <Button ALIGN=LEFT ICON="RETURN" action="bypass -h Quest ClassMaster test_server_helper001.html">Back</Button>
</body></html> </body></html>

View File

@ -1,3 +1,3 @@
<html><body>Test Server Guide:<br> <html><body>Test Server Helper:<br>
The second class transfer is only available for characters level 40 or above. The second class transfer is only available for characters level 40 or above.
</body></html> </body></html>

View File

@ -1,4 +1,4 @@
<html><body>Test Server Guide:<br> <html><body>Test Server Helper:<br>
characters level 76 or above who have completed their second occupation change can change occupations for a third time.<br> characters level 76 or above who have completed their second occupation change can change occupations for a third time.<br>
Come back after you've finished your second occupation change and reached level 76. Come back after you've finished your second occupation change and reached level 76.
</body></html> </body></html>

View File

@ -216,7 +216,7 @@ public abstract class LetterQuest extends Quest
if ((st == null) && (event.getOldLevel() < event.getNewLevel()) && canStartQuest(player)) if ((st == null) && (event.getOldLevel() < event.getNewLevel()) && canStartQuest(player))
{ {
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL); playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL);
showOnScreenMsg(player, _startMessage, ExShowScreenMessage.TOP_CENTER, 10000); showOnScreenMsg(player, _startMessage, ExShowScreenMessage.TOP_CENTER, 10000);
} }
@ -236,7 +236,7 @@ public abstract class LetterQuest extends Quest
if ((st == null) && canStartQuest(player)) if ((st == null) && canStartQuest(player))
{ {
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL); playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL);
showOnScreenMsg(player, _startMessage, ExShowScreenMessage.TOP_CENTER, 10000); showOnScreenMsg(player, _startMessage, ExShowScreenMessage.TOP_CENTER, 10000);
} }

View File

@ -367,7 +367,7 @@ public final class Q00128_PailakaSongOfIceAndFire extends Quest
if ((oldLevel < newLevel) && (newLevel == MIN_LEVEL)) if ((oldLevel < newLevel) && (newLevel == MIN_LEVEL))
{ {
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
} }
} }

View File

@ -144,7 +144,7 @@ public final class Q00386_StolenDignity extends Quest
playSound(player, QuestSound.ITEMSOUND_QUEST_ACCEPT); playSound(player, QuestSound.ITEMSOUND_QUEST_ACCEPT);
qs.setMemoState(336); qs.setMemoState(336);
qs.startQuest(); qs.startQuest();
player.sendPacket(new TutorialShowQuestionMark(336)); player.sendPacket(new TutorialShowQuestionMark(336, 1)); // why not getId() ?
playSound(player, QuestSound.ITEMSOUND_QUEST_MIDDLE); playSound(player, QuestSound.ITEMSOUND_QUEST_MIDDLE);
return "30843-05.htm"; return "30843-05.htm";
} }

View File

@ -252,7 +252,7 @@ public final class Q10301_ShadowOfTerrorBlackishRedFog extends Quest
if ((qs == null) && (event.getOldLevel() < event.getNewLevel()) && canStartQuest(player)) if ((qs == null) && (event.getOldLevel() < event.getNewLevel()) && canStartQuest(player))
{ {
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL); playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL);
} }
} }
@ -271,7 +271,7 @@ public final class Q10301_ShadowOfTerrorBlackishRedFog extends Quest
if ((qs == null) && canStartQuest(player)) if ((qs == null) && canStartQuest(player))
{ {
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL); playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL);
} }
} }

View File

@ -288,7 +288,7 @@ public class Q10304_ForForgottenHeroes extends Quest
final QuestState qs = getQuestState(player, false); final QuestState qs = getQuestState(player, false);
if ((qs == null) && (event.getOldLevel() < event.getNewLevel()) && canStartQuest(player) && (player.getLevel() >= MIN_LEVEL)) if ((qs == null) && (event.getOldLevel() < event.getNewLevel()) && canStartQuest(player) && (player.getLevel() >= MIN_LEVEL))
{ {
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL); playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL);
} }
} }
@ -301,7 +301,7 @@ public class Q10304_ForForgottenHeroes extends Quest
final QuestState qs = getQuestState(player, false); final QuestState qs = getQuestState(player, false);
if ((qs == null) && canStartQuest(player) && (player.getLevel() >= MIN_LEVEL)) if ((qs == null) && canStartQuest(player) && (player.getLevel() >= MIN_LEVEL))
{ {
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL); playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL);
} }
} }

View File

@ -474,7 +474,7 @@ public final class Q10331_StartOfFate extends Quest
if ((qs == null) && (oldLevel < newLevel) && (newLevel == MIN_LEVEL) && (player.getRace() != Race.ERTHEIA) && (player.isInCategory(CategoryType.FIRST_CLASS_GROUP))) if ((qs == null) && (oldLevel < newLevel) && (newLevel == MIN_LEVEL) && (player.getRace() != Race.ERTHEIA) && (player.isInCategory(CategoryType.FIRST_CLASS_GROUP)))
{ {
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL); playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL);
} }
} }
@ -488,7 +488,7 @@ public final class Q10331_StartOfFate extends Quest
if ((qs == null) && (player.getRace() != Race.ERTHEIA) && (player.getLevel() >= MIN_LEVEL) && (player.isInCategory(CategoryType.FIRST_CLASS_GROUP))) if ((qs == null) && (player.getRace() != Race.ERTHEIA) && (player.getLevel() >= MIN_LEVEL) && (player.isInCategory(CategoryType.FIRST_CLASS_GROUP)))
{ {
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL); playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL);
} }
} }

View File

@ -506,7 +506,7 @@ public final class Q10360_CertificationOfFate extends Quest
if ((oldLevel < newLevel) && (newLevel == MIN_LEVEL) && (player.getRace() != Race.ERTHEIA) && (player.isInCategory(CategoryType.SECOND_CLASS_GROUP))) if ((oldLevel < newLevel) && (newLevel == MIN_LEVEL) && (player.getRace() != Race.ERTHEIA) && (player.isInCategory(CategoryType.SECOND_CLASS_GROUP)))
{ {
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
} }
} }
@ -522,7 +522,7 @@ public final class Q10360_CertificationOfFate extends Quest
final QuestState st = getQuestState(player, true); final QuestState st = getQuestState(player, true);
if (st.isCreated()) if (st.isCreated())
{ {
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
} }
} }
} }

View File

@ -520,7 +520,7 @@ public final class Q10751_WindsOfFateEncounters extends Quest
else if (command.equals("Q10751_close")) else if (command.equals("Q10751_close"))
{ {
player.sendPacket(TutorialCloseHtml.STATIC_PACKET); player.sendPacket(TutorialCloseHtml.STATIC_PACKET);
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
player.clearHtmlActions(HtmlActionScope.TUTORIAL_HTML); player.clearHtmlActions(HtmlActionScope.TUTORIAL_HTML);
} }
} }
@ -538,7 +538,7 @@ public final class Q10751_WindsOfFateEncounters extends Quest
if ((st == null) && (player.getRace().equals(Race.ERTHEIA)) && (oldLevel < newLevel) && (newLevel >= MIN_LEVEL) && (player.isInCategory(CategoryType.FIRST_CLASS_GROUP))) if ((st == null) && (player.getRace().equals(Race.ERTHEIA)) && (oldLevel < newLevel) && (newLevel >= MIN_LEVEL) && (player.isInCategory(CategoryType.FIRST_CLASS_GROUP)))
{ {
showOnScreenMsg(player, NpcStringId.QUEEN_NAVARI_HAS_SENT_A_LETTER_NCLICK_THE_QUESTION_MARK_ICON_TO_READ, ExShowScreenMessage.TOP_CENTER, 10000); showOnScreenMsg(player, NpcStringId.QUEEN_NAVARI_HAS_SENT_A_LETTER_NCLICK_THE_QUESTION_MARK_ICON_TO_READ, ExShowScreenMessage.TOP_CENTER, 10000);
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
} }
} }
@ -552,7 +552,7 @@ public final class Q10751_WindsOfFateEncounters extends Quest
if ((st == null) && player.getRace().equals(Race.ERTHEIA) && (player.getLevel() >= MIN_LEVEL) && (player.isInCategory(CategoryType.FIRST_CLASS_GROUP))) if ((st == null) && player.getRace().equals(Race.ERTHEIA) && (player.getLevel() >= MIN_LEVEL) && (player.isInCategory(CategoryType.FIRST_CLASS_GROUP)))
{ {
showOnScreenMsg(player, NpcStringId.QUEEN_NAVARI_HAS_SENT_A_LETTER_NCLICK_THE_QUESTION_MARK_ICON_TO_READ, ExShowScreenMessage.TOP_CENTER, 10000); showOnScreenMsg(player, NpcStringId.QUEEN_NAVARI_HAS_SENT_A_LETTER_NCLICK_THE_QUESTION_MARK_ICON_TO_READ, ExShowScreenMessage.TOP_CENTER, 10000);
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
} }
} }
} }

View File

@ -522,7 +522,7 @@ public final class Q10752_WindsOfFateAPromise extends Quest
else if (command.equals("Q10752_close")) else if (command.equals("Q10752_close"))
{ {
player.sendPacket(TutorialCloseHtml.STATIC_PACKET); player.sendPacket(TutorialCloseHtml.STATIC_PACKET);
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
player.clearHtmlActions(HtmlActionScope.TUTORIAL_HTML); player.clearHtmlActions(HtmlActionScope.TUTORIAL_HTML);
} }
} }
@ -552,7 +552,7 @@ public final class Q10752_WindsOfFateAPromise extends Quest
{ {
showOnScreenMsg(player, NpcStringId.MASTER_KATALIN_HAS_SENT_A_LETTER_NCLICK_THE_QUESTION_MARK_ICON_TO_READ, ExShowScreenMessage.TOP_CENTER, 10000); showOnScreenMsg(player, NpcStringId.MASTER_KATALIN_HAS_SENT_A_LETTER_NCLICK_THE_QUESTION_MARK_ICON_TO_READ, ExShowScreenMessage.TOP_CENTER, 10000);
} }
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
} }
} }
@ -578,7 +578,7 @@ public final class Q10752_WindsOfFateAPromise extends Quest
{ {
showOnScreenMsg(player, NpcStringId.MASTER_KATALIN_HAS_SENT_A_LETTER_NCLICK_THE_QUESTION_MARK_ICON_TO_READ, ExShowScreenMessage.TOP_CENTER, 10000); showOnScreenMsg(player, NpcStringId.MASTER_KATALIN_HAS_SENT_A_LETTER_NCLICK_THE_QUESTION_MARK_ICON_TO_READ, ExShowScreenMessage.TOP_CENTER, 10000);
} }
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
} }
} }
} }

View File

@ -748,7 +748,7 @@ public final class Q10753_WindsOfFateChoices extends Quest
else if (command.equals("Q10753_close")) else if (command.equals("Q10753_close"))
{ {
player.sendPacket(TutorialCloseHtml.STATIC_PACKET); player.sendPacket(TutorialCloseHtml.STATIC_PACKET);
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
player.clearHtmlActions(HtmlActionScope.TUTORIAL_HTML); player.clearHtmlActions(HtmlActionScope.TUTORIAL_HTML);
} }
} }
@ -778,7 +778,7 @@ public final class Q10753_WindsOfFateChoices extends Quest
{ {
showOnScreenMsg(player, NpcStringId.MASTER_KATALIN_HAS_SENT_A_LETTER_NCLICK_THE_QUESTION_MARK_ICON_TO_READ, ExShowScreenMessage.TOP_CENTER, 10000); showOnScreenMsg(player, NpcStringId.MASTER_KATALIN_HAS_SENT_A_LETTER_NCLICK_THE_QUESTION_MARK_ICON_TO_READ, ExShowScreenMessage.TOP_CENTER, 10000);
} }
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL); playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL);
} }
} }
@ -805,7 +805,7 @@ public final class Q10753_WindsOfFateChoices extends Quest
{ {
showOnScreenMsg(player, NpcStringId.MASTER_KATALIN_HAS_SENT_A_LETTER_NCLICK_THE_QUESTION_MARK_ICON_TO_READ, ExShowScreenMessage.TOP_CENTER, 10000); showOnScreenMsg(player, NpcStringId.MASTER_KATALIN_HAS_SENT_A_LETTER_NCLICK_THE_QUESTION_MARK_ICON_TO_READ, ExShowScreenMessage.TOP_CENTER, 10000);
} }
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL); playSound(player, QuestSound.ITEMSOUND_QUEST_TUTORIAL);
} }
} }

View File

@ -357,7 +357,7 @@ public abstract class ThirdClassTransferQuest extends Quest
if ((oldLevel < newLevel) && (newLevel == _minLevel) && (player.getRace() == _race) && (player.isInCategory(CategoryType.THIRD_CLASS_GROUP))) if ((oldLevel < newLevel) && (newLevel == _minLevel) && (player.getRace() == _race) && (player.isInCategory(CategoryType.THIRD_CLASS_GROUP)))
{ {
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1));
} }
} }
@ -374,9 +374,10 @@ public abstract class ThirdClassTransferQuest extends Quest
if ((player.getLevel() >= _minLevel) && (player.getRace() == _race) && (player.isInCategory(CategoryType.THIRD_CLASS_GROUP))) if ((player.getLevel() >= _minLevel) && (player.getRace() == _race) && (player.isInCategory(CategoryType.THIRD_CLASS_GROUP)))
{ {
if (getQuestState(player, true).isCreated()) final QuestState qs = getQuestState(player, true);
if (qs.isCreated())
{ {
player.sendPacket(new TutorialShowQuestionMark(getId())); player.sendPacket(new TutorialShowQuestionMark(getId(), 1)); // Seems not to work. - blank text
} }
} }
} }

View File

@ -19,13 +19,18 @@ package com.l2jmobius.gameserver.network.serverpackets;
import com.l2jmobius.commons.network.PacketWriter; import com.l2jmobius.commons.network.PacketWriter;
import com.l2jmobius.gameserver.network.OutgoingPackets; import com.l2jmobius.gameserver.network.OutgoingPackets;
/**
* @author Mobius
*/
public final class TutorialShowQuestionMark implements IClientOutgoingPacket public final class TutorialShowQuestionMark implements IClientOutgoingPacket
{ {
private final int _markId; private final int _questId;
private final int _condition;
public TutorialShowQuestionMark(int blink) public TutorialShowQuestionMark(int questId, int condition)
{ {
_markId = blink; _questId = questId;
_condition = condition;
} }
@Override @Override
@ -33,8 +38,8 @@ public final class TutorialShowQuestionMark implements IClientOutgoingPacket
{ {
OutgoingPackets.TUTORIAL_SHOW_QUESTION_MARK.writeId(packet); OutgoingPackets.TUTORIAL_SHOW_QUESTION_MARK.writeId(packet);
packet.writeC(0x01); // Number of mark, most likely ? packet.writeC(_condition);
packet.writeD(_markId); packet.writeD(_questId);
return true; return true;
} }
} }

View File

@ -17,6 +17,7 @@
package ai.others.ClassMaster; package ai.others.ClassMaster;
import java.io.File; import java.io.File;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
@ -29,7 +30,6 @@ import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import com.l2jmobius.commons.util.IGameXmlReader; import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.commons.util.Rnd;
import com.l2jmobius.gameserver.data.xml.impl.CategoryData; import com.l2jmobius.gameserver.data.xml.impl.CategoryData;
import com.l2jmobius.gameserver.data.xml.impl.ClassListData; import com.l2jmobius.gameserver.data.xml.impl.ClassListData;
import com.l2jmobius.gameserver.data.xml.impl.SkillData; import com.l2jmobius.gameserver.data.xml.impl.SkillData;
@ -50,7 +50,7 @@ import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerLevel
import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerLogin; import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerLogin;
import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerPressTutorialMark; import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerPressTutorialMark;
import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerProfessionChange; import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerProfessionChange;
import com.l2jmobius.gameserver.model.holders.ItemChanceHolder; import com.l2jmobius.gameserver.model.holders.ItemHolder;
import com.l2jmobius.gameserver.model.spawns.SpawnTemplate; import com.l2jmobius.gameserver.model.spawns.SpawnTemplate;
import com.l2jmobius.gameserver.network.serverpackets.PlaySound; import com.l2jmobius.gameserver.network.serverpackets.PlaySound;
import com.l2jmobius.gameserver.network.serverpackets.TutorialCloseHtml; import com.l2jmobius.gameserver.network.serverpackets.TutorialCloseHtml;
@ -65,11 +65,11 @@ import ai.AbstractNpcAI;
public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
// NPCs // NPCs
private static final int[] CLASS_MASTER = private static final List<Integer> CLASS_MASTERS = new ArrayList<>();
{ {
31756, // Mr. Cat CLASS_MASTERS.add(31756); // Mr. Cat
31757, // Queen of Hearts CLASS_MASTERS.add(31757); // Queen of Hearts
}; }
// Misc // Misc
private boolean _isEnabled; private boolean _isEnabled;
private boolean _spawnClassMasters; private boolean _spawnClassMasters;
@ -80,9 +80,9 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
public ClassMaster() public ClassMaster()
{ {
load(); load();
addStartNpc(CLASS_MASTER); addStartNpc(CLASS_MASTERS);
addTalkId(CLASS_MASTER); addTalkId(CLASS_MASTERS);
addFirstTalkId(CLASS_MASTER); addFirstTalkId(CLASS_MASTERS);
} }
@Override @Override
@ -128,8 +128,8 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
if ("classChangeOption".equals(c.getNodeName())) if ("classChangeOption".equals(c.getNodeName()))
{ {
final List<CategoryType> appliedCategories = new LinkedList<>(); final List<CategoryType> appliedCategories = new LinkedList<>();
final List<ItemChanceHolder> requiredItems = new LinkedList<>(); final List<ItemHolder> requiredItems = new LinkedList<>();
final List<ItemChanceHolder> rewardedItems = new LinkedList<>(); final List<ItemHolder> rewardedItems = new LinkedList<>();
boolean setNoble = false; boolean setNoble = false;
boolean setHero = false; boolean setHero = false;
final String optionName = parseString(attrs, "name", ""); final String optionName = parseString(attrs, "name", "");
@ -163,9 +163,8 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
final int itemId = parseInteger(attrs, "id"); final int itemId = parseInteger(attrs, "id");
final int count = parseInteger(attrs, "count", 1); final int count = parseInteger(attrs, "count", 1);
final int chance = parseInteger(attrs, "chance", 100);
rewardedItems.add(new ItemChanceHolder(itemId, chance, count)); rewardedItems.add(new ItemHolder(itemId, count));
} }
else if ("setNoble".equals(r.getNodeName())) else if ("setNoble".equals(r.getNodeName()))
{ {
@ -186,9 +185,8 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
final int itemId = parseInteger(attrs, "id"); final int itemId = parseInteger(attrs, "id");
final int count = parseInteger(attrs, "count", 1); final int count = parseInteger(attrs, "count", 1);
final int chance = parseInteger(attrs, "chance", 100);
requiredItems.add(new ItemChanceHolder(itemId, chance, count)); requiredItems.add(new ItemHolder(itemId, count));
} }
} }
} }
@ -251,7 +249,7 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
case "buyitems": case "buyitems":
{ {
htmltext = npc.getId() == CLASS_MASTER[0] ? "test_server_helper001a.html" : "test_server_helper001b.html"; htmltext = npc.getId() == CLASS_MASTERS.get(0) ? "test_server_helper001a.html" : "test_server_helper001b.html";
break; break;
} }
case "setnoble": case "setnoble":
@ -374,31 +372,22 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
htmltext = htmltext.replace("%options%", getClassChangeOptions(player, classId)); htmltext = htmltext.replace("%options%", getClassChangeOptions(player, classId));
return htmltext; return htmltext;
} }
}
final ClassChangeData data = getClassChangeData(classDataIndex);
if (data == null) final ClassChangeData data = getClassChangeData(classDataIndex);
if ((data != null) && (data.getItemsRequired().size() > 0))
{
for (ItemHolder ri : data.getItemsRequired())
{ {
return null; if (player.getInventory().getInventoryItemCount(ri.getId(), -1) < ri.getCount())
{
player.sendMessage("You do not have enough items.");
return null; // No class change if payment failed.
}
} }
for (ItemHolder ri : data.getItemsRequired())
//@formatter:off
final boolean paid = data.getItemsRequired().stream()
.filter(ich -> ich.getChance() > Rnd.get(100)) // Chance to pay the price
.filter(ih -> player.getInventory().getInventoryItemCount(ih.getId(), -1) >= ih.getCount())
.allMatch(ih -> player.destroyItemByItemId(getClass().getSimpleName(), ih.getId(), ih.getCount(), npc, true));
//@formatter:on
if (paid)
{ {
//@formatter:off player.destroyItemByItemId(getClass().getSimpleName(), ri.getId(), ri.getCount(), npc, true);
data.getItemsRewarded().stream()
.filter(ich -> ich.getChance() > Rnd.get(100)) // Chance to receive the reward
.forEach(ih -> player.addItem(getClass().getSimpleName(), ih.getId(), ih.getCount(), npc, true));
//@formatter:on
}
else
{
return null; // No class change if payment failed.
} }
} }
@ -463,6 +452,14 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
} }
break; break;
} }
case "test_server_helper001.html":
{
if (CLASS_MASTERS.contains(npc.getId()))
{
htmltext = event;
}
break;
}
} }
return htmltext; return htmltext;
@ -906,14 +903,7 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
option.getItemsRequired().forEach(ih -> option.getItemsRequired().forEach(ih ->
{ {
if (ih.getChance() >= 100) sb.append("<tr><td><font color=\"LEVEL\">" + ih.getCount() + "</font></td><td>" + ItemTable.getInstance().getTemplate(ih.getId()).getName() + "</td><td width=30></td></tr>");
{
sb.append("<tr><td><font color=\"LEVEL\">" + ih.getCount() + "</font></td><td>" + ItemTable.getInstance().getTemplate(ih.getId()).getName() + "</td><td width=30></td></tr>");
}
else
{
sb.append("<tr><td><font color=\"LEVEL\">" + ih.getCount() + "</font></td><td>" + ItemTable.getInstance().getTemplate(ih.getId()).getName() + "</td><td width=30><font color=LEVEL>" + ih.getChance() + "%</font></td></tr>");
}
}); });
} }
sb.append("<tr><td>Rewards:</td></tr>"); sb.append("<tr><td>Rewards:</td></tr>");
@ -938,14 +928,7 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
{ {
option.getItemsRewarded().forEach(ih -> option.getItemsRewarded().forEach(ih ->
{ {
if (ih.getChance() >= 100) sb.append("<tr><td><font color=\"LEVEL\">" + ih.getCount() + "</font></td><td>" + ItemTable.getInstance().getTemplate(ih.getId()).getName() + "</td><td width=30></td></tr>");
{
sb.append("<tr><td><font color=\"LEVEL\">" + ih.getCount() + "</font></td><td>" + ItemTable.getInstance().getTemplate(ih.getId()).getName() + "</td><td width=30></td></tr>");
}
else
{
sb.append("<tr><td><font color=\"LEVEL\">" + ih.getCount() + "</font></td><td>" + ItemTable.getInstance().getTemplate(ih.getId()).getName() + "</td><td width=30><font color=LEVEL>" + ih.getChance() + "%</font></td></tr>");
}
}); });
if (option.isRewardNoblesse()) if (option.isRewardNoblesse())
@ -971,8 +954,8 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
private final List<CategoryType> _appliedCategories; private final List<CategoryType> _appliedCategories;
private boolean _rewardNoblesse; private boolean _rewardNoblesse;
private boolean _rewardHero; private boolean _rewardHero;
private List<ItemChanceHolder> _itemsRequired; private List<ItemHolder> _itemsRequired;
private List<ItemChanceHolder> _itemsRewarded; private List<ItemHolder> _itemsRewarded;
public ClassChangeData(String name, List<CategoryType> appliedCategories) public ClassChangeData(String name, List<CategoryType> appliedCategories)
{ {
@ -1026,22 +1009,22 @@ public final class ClassMaster extends AbstractNpcAI implements IGameXmlReader
_rewardHero = rewardHero; _rewardHero = rewardHero;
} }
void setItemsRequired(List<ItemChanceHolder> itemsRequired) void setItemsRequired(List<ItemHolder> itemsRequired)
{ {
_itemsRequired = itemsRequired; _itemsRequired = itemsRequired;
} }
public List<ItemChanceHolder> getItemsRequired() public List<ItemHolder> getItemsRequired()
{ {
return _itemsRequired != null ? _itemsRequired : Collections.emptyList(); return _itemsRequired != null ? _itemsRequired : Collections.emptyList();
} }
void setItemsRewarded(List<ItemChanceHolder> itemsRewarded) void setItemsRewarded(List<ItemHolder> itemsRewarded)
{ {
_itemsRewarded = itemsRewarded; _itemsRewarded = itemsRewarded;
} }
public List<ItemChanceHolder> getItemsRewarded() public List<ItemHolder> getItemsRewarded()
{ {
return _itemsRewarded != null ? _itemsRewarded : Collections.emptyList(); return _itemsRewarded != null ? _itemsRewarded : Collections.emptyList();
} }

View File

@ -1,12 +1,12 @@
<html><body>Test Server Helper:<br> <html><body>Test Server Helper:<br>
How may I help you?<br> How may I help you?<br>
<font color="LEVEL">*About haracters*</font><br> <font color="LEVEL">*About characters*</font><br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster firstclass">"I want to complete the first class transfer."</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster firstclass">"I want to complete the first class transfer."</Button>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster secondclass">"I want to complete the second class transfer."</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster secondclass">"I want to complete the second class transfer."</Button>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster thirdclass">"I want to complete the third class transfer."</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster thirdclass">"I want to complete the third class transfer."</Button>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster awaken">"I'm interested in the 4th class transfer (Awaken!)."</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster awaken">"I'm interested in the 4th class transfer (Awaken!)."</Button>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster setnoble">"I want to become a Noblesse!"</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster setnoble">"I want to become a Noblesse!"</Button>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster ask=-6&reply=2">"I want to acquire the Exalted status."</Button> <!--<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster ask=-6&reply=2">"I want to acquire the Exalted status."</Button>-->
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster learnskills">"I want to learn all skills up to the third class transfer."</Button><br> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster learnskills">"I want to learn all skills up to the third class transfer."</Button><br>
<font color="LEVEL">*About Items*</font><br> <font color="LEVEL">*About Items*</font><br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Multisell 697">"I need some gear."</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Multisell 697">"I need some gear."</Button>

View File

@ -1,3 +1,3 @@
<html><body>Test Server Guide:<br> <html><body>Test Server Helper:<br>
You've already attempted the third class transfer! You've already attempted the third class transfer!
</body></html> </body></html>

View File

@ -1,3 +1,3 @@
<html><body>Test Server Guide:<br> <html><body>Test Server Helper:<br>
You have successfully transferred class! You have successfully transferred class!
</body></html> </body></html>

View File

@ -2,5 +2,5 @@
Do you want to raise your clan's level?<br> Do you want to raise your clan's level?<br>
You can raise it <font color="LEVEL">up to level 10</font>, using the test server helper.<br> You can raise it <font color="LEVEL">up to level 10</font>, using the test server helper.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster clanlevelup">"I want to level up."</Button> <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h Quest ClassMaster clanlevelup">"I want to level up."</Button>
<Button ALIGN=LEFT ICON="RETURN" action="link test_server_helper001.htm">Back</Button> <Button ALIGN=LEFT ICON="RETURN" action="bypass -h Quest ClassMaster test_server_helper001.html">Back</Button>
</body></html> </body></html>

View File

@ -1,3 +1,3 @@
<html><body>Test Server Guide:<br> <html><body>Test Server Helper:<br>
The second class transfer is only available for characters level 40 or above. The second class transfer is only available for characters level 40 or above.
</body></html> </body></html>

View File

@ -1,4 +1,4 @@
<html><body>Test Server Guide:<br> <html><body>Test Server Helper:<br>
characters level 76 or above who have completed their second occupation change can change occupations for a third time.<br> characters level 76 or above who have completed their second occupation change can change occupations for a third time.<br>
Come back after you've finished your second occupation change and reached level 76. Come back after you've finished your second occupation change and reached level 76.
</body></html> </body></html>