Hi,
I have created custom `ScriptBypassHandler` and `ScriptItemHandler`, the item works properly, it displays the html chat, but whenever I click the button which should trigger my custom bypass handler, it does nothing. No error in console, no `handle` method being called. Here is my code:
and
What I'm doing wrong? What I'am missing?
I have created custom `ScriptBypassHandler` and `ScriptItemHandler`, the item works properly, it displays the html chat, but whenever I click the button which should trigger my custom bypass handler, it does nothing. No error in console, no `handle` method being called. Here is my code:
Java:
package com.l2horizon.CustomQuestsExt.handlers.item;
import handler.items.ScriptItemHandler;
import l2.gameserver.model.Playable;
import l2.gameserver.model.Player;
import l2.gameserver.model.base.ClassId;
import l2.gameserver.model.items.ItemInstance;
import l2.gameserver.network.l2.s2c.NpcHtmlMessage;
public class FirstClassChangeItem extends ScriptItemHandler {
@Override
public int[] getItemIds() {
return new int[] { 91706 };
}
@Override
public boolean useItem(Playable playable, ItemInstance item, boolean arg2) {
if (playable instanceof Player player) {
// Check if player is in base class (level 1)
if (player.getClassId().getLevel() != 1) {
player.sendMessage("This scroll can only be used for first class change!");
return false;
}
// Check if player meets level requirement
if (player.getLevel() < 20) {
player.sendMessage("You must be at least level 20 to change your class!");
return false;
}
// Show class change HTML
showClassChangeHTML(player);
return true;
}
return false;
}
private void showClassChangeHTML(Player player) {
final NpcHtmlMessage html = new NpcHtmlMessage(5);
final StringBuilder sb = new StringBuilder(500);
sb.append("<html><body><center>");
sb.append("<font color=\"LEVEL\">First Class Change</font><br1>");
sb.append("Select your new class:<br>");
sb.append("<font color=\"FFAA00\">Cost: 100,000 Adena</font><br1>");
final ClassId currentClassId = player.getClassId();
// Collect available classes
java.util.List<ClassId> availableClasses = new java.util.ArrayList<>();
for (ClassId cid : ClassId.VALUES) {
// Only show level 2 classes (first class change)
if (cid.getLevel() != 2) {
continue;
}
// Check if this class is a valid child of current class
if (validateClassId(currentClassId, cid)) {
availableClasses.add(cid);
}
}
if (availableClasses.isEmpty()) {
sb.append("No available classes found for your character.<br>");
} else {
sb.append("<table width=270 border=0 cellspacing=0 cellpadding=1>");
int count = 0;
for (ClassId cid : availableClasses) {
String className = ClassId.getClassById(cid.getId()).name();
// Format class name: split by underscore and capitalize each word
String[] words = className.toLowerCase().split("_");
StringBuilder formattedName = new StringBuilder();
for (String word : words) {
if (word.length() > 0) {
formattedName.append(Character.toUpperCase(word.charAt(0)))
.append(word.substring(1))
.append(" ");
}
}
className = formattedName.toString().trim();
if (count % 2 == 0) {
sb.append("<tr>");
}
sb.append("<td width=135 align=center>")
.append("<button value=\"").append(className)
.append("\" action=\"bypass _first_class_change_scroll ").append(cid.getId())
.append("\" width=130 height=20 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_CT1.Button_DF\">")
.append("</td>");
count++;
if (count % 2 == 0) {
sb.append("</tr>");
}
}
// Close row if odd number of items
if (count % 2 != 0) {
sb.append("<td width=135></td></tr>");
}
sb.append("</table>");
}
sb.append("</center></body></html>");
html.setHtml(sb.toString());
player.sendPacket(html);
}
/**
* Returns true if class change is possible
*
* @param oldCID current player ClassId
* @param newCID new ClassId
* @return true if class change is possible
*/
private static boolean validateClassId(ClassId oldCID, ClassId newCID) {
if (newCID == null) {
return false;
}
if (oldCID == newCID.getParent()) {
return true;
}
return false;
}
}
and
Java:
package com.l2horizon.CustomQuestsExt.handlers.bypass;
import handler.bypass.ScriptBypassHandler;
import l2.gameserver.model.Player;
import l2.gameserver.model.base.ClassId;
import l2.gameserver.model.instances.NpcInstance;
import l2.gameserver.model.items.ItemInstance;
import l2.gameserver.network.l2.s2c.NpcHtmlMessage;
/**
* Bypass handler for first class change scroll. Handles
* "_first_class_change_scroll" bypass commands to perform class changes.
*/
public class FirstClassChangeBypassHandler extends ScriptBypassHandler {
private static final int FIRST_CLASS_CHANGE_SCROLL_ID = 91706;
private static final int ADENA_ID = 57;
private static final long ADENA_COST = 100000;
@Override
public void handle(Player player, NpcInstance npc, String bypass, String params) {
System.out.println("handle: " + bypass + ", " + params);
String classIdStr = params.trim();
if (classIdStr.isEmpty()) {
return;
}
try {
int newClassId = Integer.parseInt(classIdStr);
// Check if player has the First Class Change Scroll
ItemInstance scroll = player.getInventory().getItemByItemId(FIRST_CLASS_CHANGE_SCROLL_ID);
if (scroll == null) {
player.sendMessage("You need a First Class Change Scroll to change your class!");
return;
}
// Check if player has enough Adena
ItemInstance adena = player.getInventory().getItemByItemId(ADENA_ID);
if (adena == null || adena.getCount() < ADENA_COST) {
player.sendMessage("You need " + ADENA_COST + " Adena to change your class!");
return;
}
// Validate player can do first class change
if (player.getClassId().getLevel() != 1) {
player.sendMessage("You are not eligible for first class change!");
return;
}
if (player.getLevel() < 20) {
player.sendMessage("You must be at least level 20 to change your class!");
return;
}
// Validate the selected class
ClassId newClass = ClassId.getClassById(newClassId);
if (newClass == null || newClass.getLevel() != 2) {
player.sendMessage("Invalid class selection!");
return;
}
// Check if this is a valid class change
if (!validateClassId(player.getClassId(), newClass)) {
player.sendMessage("You cannot change to this class!");
return;
}
// Perform the class change
if (checkAndChangeClass(player, newClassId)) {
// Delete the scroll and Adena from inventory
player.getInventory().destroyItem(scroll, 1L);
player.getInventory().destroyItemByItemId(ADENA_ID, ADENA_COST);
player.sendMessage("Congratulations! You have changed to " + getFormattedClassName(newClass) + "!");
// Show success HTML
final NpcHtmlMessage html = new NpcHtmlMessage(5);
html.setHtml("<html><body><center>First Class Change:<br><br>" + "Congratulations!<br>"
+ "You have successfully changed to <font color=\"LEVEL\">" + getFormattedClassName(newClass)
+ "</font>!<br><br>" + "Your new powers await you!<br>" + "</center></body></html>");
player.sendPacket(html);
} else {
player.sendMessage("Failed to change class. Please contact an administrator.");
}
} catch (NumberFormatException e) {
player.sendMessage("Invalid class selection!");
}
}
/**
* Returns true if class change is possible
*
* @param oldCID current player ClassId
* @param newCID new ClassId
* @return true if class change is possible
*/
private static boolean validateClassId(ClassId oldCID, ClassId newCID) {
if (newCID == null) {
return false;
}
if (oldCID == newCID.getParent()) {
return true;
}
return false;
}
/**
* Performs the class change
*
* @param player the player
* @param newClassID the new class ID
* @return true if successful
*/
private static boolean checkAndChangeClass(Player player, int newClassID) {
final ClassId currentClassID = player.getClassId();
// For first class change, must be level 1 and at least level 20
if (currentClassID.getLevel() != 1 || player.getLevel() < 20) {
return false;
}
player.setClassId(newClassID, false, false);
player.broadcastCharInfo();
return true;
}
/**
* Formats the class name for display
*
* @param classId the class ID
* @return formatted class name
*/
private static String getFormattedClassName(ClassId classId) {
String className = classId.name();
String[] words = className.toLowerCase().split("_");
StringBuilder formattedName = new StringBuilder();
for (String word : words) {
if (word.length() > 0) {
formattedName.append(Character.toUpperCase(word.charAt(0))).append(word.substring(1)).append(" ");
}
}
return formattedName.toString().trim();
}
@Override
public boolean requiresNpc() {
return false;
}
@Override
public boolean requiresNpcCheck() {
return false;
}
@Override
public String[] getBypassPrefixes() {
return new String[] { "_first_class_change_scroll " };
}
}
What I'm doing wrong? What I'am missing?

