Noticias:

Debes de estar registrado para poder ver el contenido indicado. Registrate o Conectate

Menú Principal

Crear nuevo tipo de Npc: Recipe Seller (Kimeraweb)

Iniciado por Swarlog, Ago 04, 2022, 01:20 AM

Tema anterior - Siguiente tema

Swarlog

Aquí vengo con otra creación exclusivamente para los miembros de L2DevsAdmins. No posteo mis códigos en otros foros  ;)

Este patch crea un tipo nuevo de NPC, lo convierte en un vendedor de recipes.

Funciona de una manera muy sencilla.

Lo primero que necesitamos es un NPC, yo cloné un enano de la warehouse en la tabla de npc_customs con el número 1000004.

Luego, un HTML, para que cuando le cliquen, muestre un texto inicial:
Código (html5) [Seleccionar]
<html>
<head><title>RecipeSeller</title></head>
<body>
Desde tiempos inmemoriales mi familia ha ido coleccionando las recetas mas exitosas de todos los tiempos.<br>
Gracias a mi, han surgido grandes guerreros y magos poderosos como dioses.<br>
Pero no creas que es tan facil, todos no tienen la habilidad de entender mis recetas. Te lo advierto porque no acepto devoluciones... tsk tsk...<br>
No serias el primero que copia mi receta y luego pretende que le devuelva el importe!<br><br>
<a action="bypass -h npc_%objectId%_verRecipesAlfabeticamente">Muestrame los recipes por orden alfabetico.</a><br>

</body>
</html>

Y luego editar el core.

En este fichero, que tendremos que crear nuevo, está la clase RecipeObject. Se trata del recipe cargado en la RAM pero transformado en Objecto, con funciones para que pueda desarrollar varios tipos de trabajo.
Código (javascript) [Seleccionar]
### Eclipse Workspace Patch 1.0
#P L2J_Server
Index: java/com/l2jrol/RecipeObject.java
===================================================================
--- java/com/l2jrol/RecipeObject.java (revision 0)
+++ java/com/l2jrol/RecipeObject.java (working copy)
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2004-2014 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server 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.
+ *
+ * L2J Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jrol;
+
+import com.l2jserver.gameserver.datatables.ItemTable;
+import com.l2jserver.gameserver.model.items.L2Item;
+
+/**
+ * @author Kimeraweb
+ */
+public class RecipeObject
+{
+ int _id, _level, _craftLevel, _rate, _productionID, _crystalizedCount;
+ String _name, _recipeIcon, _coinIcon, _productionIcon, _nameCoin;
+
+ // Custom
+ long _price = 0; // Cantidad de objetos necesarios para obtener el recipe
+ int _idCoin = 0; // ID del objeto usado como moneda de comercio
+
+ /**
+ * @param id
+ * @param craftLevel
+ * @param name
+ * @param rate
+ * @param productionID
+ */
+ public RecipeObject(int id, int craftLevel, String name, int rate, int productionID)
+ {
+ _id = id;
+ _craftLevel = craftLevel;
+ _name = name;
+ _rate = rate;
+ _productionID = productionID;
+ setCrystalizedCount(); // Obtiene el numero de cristales obtenido al cristalizar el objeto producido
+ setLevel(); // Estable el level del recipe en base al objeto producido
+ setPrice(); // Establece el precio por la referencia del item
+ setIconCoin(); // Establece el icono de la moneda
+ setIconRecipe(); // Establece el icono del recipe
+ setIconProduction(); // Establece el icono del objeto que se produce
+ }
+
+ /**
+ * @param id
+ * @param craftLevel
+ * @param name
+ * @param rate
+ * @param productionID
+ * @param price
+ * @param idCoin
+ * @param nameCoin
+ */
+ public RecipeObject(int id, int craftLevel, String name, int rate, int productionID, int price, int idCoin, String nameCoin)
+ {
+ _id = id;
+ _craftLevel = craftLevel;
+ _name = name;
+ _rate = rate;
+ _productionID = productionID;
+ _price = price;
+ _idCoin = idCoin;
+ setLevel(); // Estable el level del recipe en base al objeto producido
+ setIconRecipe(); // Establece el icono del recipe
+ setIconCoin(); // Establece el icono de la moneda
+ setIconProduction(); // Establece el icono del objeto que se produce
+ _nameCoin = nameCoin;
+ }
+
+ public int getID()
+ {
+ return _id;
+ }
+
+ public int getLevel()
+ {
+ return _level;
+ }
+
+ public int getCraftLevel()
+ {
+ return _craftLevel;
+ }
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ public int getRate()
+ {
+ return _rate;
+ }
+
+ public long getPrice()
+ {
+ return _price;
+ }
+
+ public int getIdCoin()
+ {
+ return _idCoin;
+ }
+
+ public String getIconLevel()
+ {
+ String icon = "";
+
+ switch (_level)
+ {
+ case 0:
+ icon = "<img src=\"L2UI_CT1.Button_DF_Right\" width=16 height=16>";
+ break;
+ case 1:
+ icon = "<img src=\"L2UI_CT1.Icon_DF_ItemGrade_D\" width=16 height=16>";
+ break;
+ case 2:
+ icon = "<img src=\"L2UI_CT1.Icon_DF_ItemGrade_C\" width=16 height=16>";
+ break;
+ case 3:
+ icon = "<img src=\"L2UI_CT1.Icon_DF_ItemGrade_B\" width=16 height=16>";
+ break;
+ case 4:
+ icon = "<img src=\"L2UI_CT1.Icon_DF_ItemGrade_A\" width=16 height=16>";
+ break;
+ case 5:
+ icon = "<img src=\"L2UI_CT1.Icon_DF_ItemGrade_S\" width=16 height=16>";
+ break;
+ case 6:
+ icon = "<img src=\"L2UI_CT1.Icon_DF_ItemGrade_80\" width=16 height=16>";
+ break;
+ case 7:
+ icon = "<img src=\"L2UI_CT1.Icon_DF_ItemGrade_84\" width=16 height=16>";
+ break;
+ default:
+ icon = "<img src=\"L2UI_CH3.aboutotpicon\" width=16 height=16>";
+ break;
+ }
+
+ return icon;
+ }
+
+ public String getIconRecipe()
+ {
+ return _recipeIcon;
+ }
+
+ public String getIconCoin()
+ {
+ return _coinIcon;
+ }
+
+ public String getIconProduction()
+ {
+ return _productionIcon;
+ }
+
+ public String getNameCoin()
+ {
+ return _nameCoin;
+ }
+
+ /**
+ * El precio se establece de esta manera: Los No Grade se establecen por su precio de venta * 10, se pagan adenas. Los Grados D -> S84 se establecen por los cristales que obtienes al cristalizar el item * nivel de crafteo requerido (El nivel de crafteo llega a 10). Se paga en cristales de ese
+ * tipo.
+ */
+ private void setPrice()
+ {
+ // Precio en adenas obtenidos en la tienda
+ int precio = ItemTable.getInstance().getTemplate(_id).getReferencePrice();
+
+ if (precio == 0)
+ {
+ precio = 15000;
+ }
+
+ switch (_level)
+ {
+ case 0:
+ // No grade
+ _price = precio * 10;
+ _idCoin = 57; // Adena
+ _nameCoin = "adenas";
+ break;
+
+ case 1:
+ // D Grade, nivel bajo
+ _price = ((_crystalizedCount * _craftLevel) > 0) ? _crystalizedCount * _craftLevel * (_rate / 10) : _craftLevel * _rate;
+ _idCoin = 1459; // Crystal D
+ _nameCoin = "cristales D Grade";
+ break;
+
+ case 2:
+ // C Grade, nivel alto
+ _price = ((_crystalizedCount * _craftLevel) > 0) ? _crystalizedCount * _craftLevel * (_rate / 10) : _craftLevel * _rate;
+ _idCoin = 1459; // Crystal D
+ _nameCoin = "cristales C Grade";
+ break;
+
+ case 3:
+ // B Grade, nivel bajo
+ _price = ((_crystalizedCount * _craftLevel) > 0) ? _crystalizedCount * _craftLevel * (_rate / 10) : _craftLevel * _rate;
+ _idCoin = 1460; // Crystal B
+ _nameCoin = "cristales B Grade";
+ break;
+
+ case 4:
+ // A Grade, nivel alto
+ _price = ((_crystalizedCount * _craftLevel) > 0) ? _crystalizedCount * _craftLevel * (_rate / 10) : _craftLevel * _rate;
+ _idCoin = 1461; // Crystal A
+ _nameCoin = "cristales A Grade";
+ break;
+
+ case 5:
+ // S Grade
+ _price = ((_crystalizedCount * _craftLevel) > 0) ? _crystalizedCount * _craftLevel * (_rate / 10) : _craftLevel * _rate;
+ _idCoin = 1462; // Crystal S
+ _nameCoin = "cristales S Grade";
+ break;
+
+ case 6:
+ // S80 Grade
+ _price = ((_crystalizedCount * _craftLevel) > 0) ? _crystalizedCount * _craftLevel * (_rate / 10) : _craftLevel * _rate;
+ _idCoin = 1462; // Crystal S
+ _nameCoin = "cristales S Grade";
+ break;
+
+ case 7:
+ // S84 Grade
+ _price = ((_crystalizedCount * _craftLevel) > 0) ? _crystalizedCount * _craftLevel * (_rate / 10) : _craftLevel * _rate;
+ _idCoin = 1462; // Crystal S
+ _nameCoin = "cristales S Grade";
+ break;
+
+ default:
+ // Error
+ System.out.println("No se pudo establecer un precio a " + _name);
+ break;
+ }
+
+ }
+
+ private void setLevel()
+ {
+ L2Item item = ItemTable.getInstance().getTemplate(_productionID);
+ _level = item.getItemGrade(); // Del 0 al 7
+ }
+
+ private void setCrystalizedCount()
+ {
+ L2Item item = ItemTable.getInstance().getTemplate(_productionID);
+ _crystalizedCount = item.getCrystalCount();
+ }
+
+ // Creando los iconos
+
+ private void setIconRecipe()
+ {
+ L2Item item = ItemTable.getInstance().getTemplate(_id);
+ _recipeIcon = "<img src=\"" + item.getIcon() + "\" width=32 height=32>";
+ }
+
+ private void setIconCoin()
+ {
+ L2Item item = ItemTable.getInstance().getTemplate(_idCoin);
+ _coinIcon = "<img src=\"" + item.getIcon() + "\" width=32 height=32>";
+ }
+
+ private void setIconProduction()
+ {
+ L2Item item = ItemTable.getInstance().getTemplate(_productionID);
+ _productionIcon = "<img src=\"" + item.getIcon() + "\" width=32 height=32>";
+ }
+
+}

El siguiente es la entidad RecipeSeller, otra clase nueva que tendremos que crear. Aquí recibiremos los bypass generados en los cuadros de texto HTML.
Código (javascript) [Seleccionar]
Index: java/com/l2jserver/gameserver/model/entity/RecipeSeller.java
===================================================================
--- java/com/l2jserver/gameserver/model/entity/RecipeSeller.java (revision 0)
+++ java/com/l2jserver/gameserver/model/entity/RecipeSeller.java (working copy)
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2004-2014 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server 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.
+ *
+ * L2J Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.entity;
+
+import javolution.text.TextBuilder;
+
+import com.l2jrol.RecipeObject;
+import com.l2jrol.RecipeSeller_Handler;
+import com.l2jserver.gameserver.model.actor.instance.L2NpcInstance;
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.model.itemcontainer.PcInventory;
+import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
+import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
+
+/**
+ * @author Kimeraweb
+ */
+public class RecipeSeller
+{
+ static double _limiteRecipesPorPagina = 20.0d;
+
+ public static synchronized void onBypass(String command, L2PcInstance playerInstance)
+ {
+
+ if (command.contains("verRecipes"))
+ {
+ String[] comando = command.split("_");
+
+ // Bypass tipo: bypass -h npc_%objectId%_verRecipesAlfabeticamente
+ if (comando.length == 1)
+ {
+ switch (comando[0])
+ {
+ case "verRecipesAlfabeticamente":
+ mostrarOrdenAlfabetico(playerInstance);
+ break;
+
+ default:
+ System.out.println("No existe el bypass " + command + " en RecipeSeller.java");
+ break;
+ }
+ }
+
+ // @Deprecated. Bypass tipo: bypass -h npc_%objectId%_verRecipesAlfabeticamente_A
+ else if (comando.length == 2)
+ {
+ // mostrarListaRecipesLetra(playerInstance, comando[1]);
+ System.out.println("Solucion sustituida por la expresion letra_pagina.");
+ }
+
+ // Bypass tipo: bypass -h npc_%objectId%_verRecipesAlfabeticamente_A_10
+ else if (comando.length == 3)
+ {
+ // comando[1] envia la letra, comando[2] envia la pagina a mostrar
+ mostrarListaRecipesLetraPagina(playerInstance, comando[1], comando[2]);
+ }
+ }
+ // Bypass tipo: bypass -h npc_%objectId%_comprarRecipe_1210
+ else if (command.contains("comprarRecipe"))
+ {
+ String[] comando = command.split("_");
+
+ comprarArticulo(playerInstance, comando[1]);
+
+ }
+ else
+ {
+ System.out.println("Bypass " + command + " no reconocido.");
+ }
+
+ }
+
+ @SuppressWarnings("null")
+ private static void comprarArticulo(L2PcInstance pj, String articulo)
+ {
+ RecipeObject recipe = null;
+
+ for (RecipeObject ro : RecipeSeller_Handler._FASTLIST_RECIPEOBJECT)
+ {
+ if (ro.getID() == Integer.parseInt(articulo))
+ {
+ recipe = ro;
+ break;
+ }
+ }
+
+ if (recipe == null)
+ {
+ System.out.println("El objetoID " + articulo + " no se encuentra en la lista.");
+ return;
+ }
+
+ PcInventory inventarioPj = pj.getInventory();
+ long cantidadDinero = inventarioPj.getInventoryItemCount(recipe.getIdCoin(), 0);
+
+ L2ItemInstance[] dineroInstance = inventarioPj.getAllItemsByItemId(recipe.getIdCoin());
+
+ NpcHtmlMessage npcReply = new NpcHtmlMessage(5);
+ TextBuilder html = new TextBuilder("<html><title>Recipe Seller</title><body>");
+
+ if (cantidadDinero >= recipe.getPrice())
+ {
+ if (pj.destroyItem("L2RecipeSeller", dineroInstance[0].getObjectId(), recipe.getPrice(), pj.getTarget(), true))
+ {
+ pj.addItem("L2RecipeSeller", recipe.getID(), 1, pj, true);
+
+ // Mensaje de compra realizada
+ html.append("Enhorabuena <font color=\"LEVEL\">" + pj.getName() + "</font>.<br><br>");
+ html.append("Has adquirido un <font color=\"LEVEL\">" + recipe.getName() + "</font> a cambio de <font color=\"LEVEL\">" + recipe.getPrice() + " " + recipe.getNameCoin() + "</font>.");
+ html.append(" Personalmente creo que has pillado el mejor precio del mercado. La demanda de recipes va en aumento y me vere obligado a subir los precios.<br><br>");
+ html.append("Es el mercado, la oferta y la demanda... si la produccion no da a basto, hay que subir los precios, eso es hijo. No me hago rico porque quiera...");
+ html.append("hay tantas cosas que pagar... el papel, los conocimientos, los sabios, los peligros... espero que valores tu compra tanto como yo.<br><br>");
+ html.append("Si ya no te interesan mas articulos, te ruego que te vayas. Las colas son malas para las clientes, a nadie le gusta esperar sabes?<br><br>");
+ html.append("Espero verte pronto! Y por favor, si necesitas mas recipes, buscame!! Siempre estoy de aqui para alla... buscando nuevos recipes que vender!");
+ }
+ else
+ {
+ System.out.println("Error en RecipeSeller. El ObjectID " + dineroInstance[0].getItemId() + " deberia corresponder a " + recipe.getIdCoin());
+ html.append("Aviso para el administrador. Por favor, informe de este error al administrador del server.<br>Disculpe las molestias");
+ }
+ }
+ else
+ {
+ // Mensaje de compra abortada
+ html.append("Ah madre de Dios...!<br><br>");
+ html.append("Solo puedes darme <font color=\"LEVEL\">" + cantidadDinero + "</font> de <font color=\"LEVEL\">" + recipe.getNameCoin() + "</font>?<br><br>");
+ html.append("Mis precios no son negociables!! Donde has leido que esto sea una subasta! Acaso no valoras mis articulos?<br>");
+ html.append("Por favor, no me ofendas con tu palabreria.<br>");
+ html.append("Vuelve aqui cuando hayas reunido <font color=\"LEVEL\">" + recipe.getPrice() + "</font> de <font color=\"LEVEL\">" + recipe.getNameCoin() + "</font> si quieres comprar un " + recipe.getName() + ".<br><br>");
+ html.append("(Murmurando) Grrr... estos crios no saben apreciar el tiempo invertido en reunir esta fuente de sabiduria...");
+ }
+ npcReply.setHtml(html.toString());
+ pj.sendPacket(npcReply);
+ }
+
+ /**
+ * Este metodo muestra los recipes de la letra suministrada y la pagina suministrada
+ * @param activeChar
+ * @param letra
+ * @param pagina
+ */
+ private static void mostrarListaRecipesLetraPagina(L2PcInstance activeChar, String letra, String pagina)
+ {
+ int contador = 0, // cuenta los recipes mostrados
+ cuentaRecipes = 0, // cuenta los recipes recorridos en la coleccion
+ mostrarRecipesDesde = (int) (Integer.valueOf(pagina) * _limiteRecipesPorPagina); // Asigna el valor de inicio para contar los recipes
+ String recipe_recipes = (int) RecipeSeller_Handler.mostrarCantidadLetra(letra) == 1 ? "recipe" : "recipes";
+ NpcHtmlMessage npcReply = new NpcHtmlMessage(5);
+
+ TextBuilder html = new TextBuilder("<html><title>Recipe Seller - Letra " + letra + "</title><body>");
+ html.append("<center>Actualmente poseo <font color=\"FFFF00\">" + (int) RecipeSeller_Handler.mostrarCantidadLetra(letra) + " " + recipe_recipes + "</font> con la letra " + letra + ".<br>");
+
+ for (RecipeObject r : RecipeSeller_Handler._FASTLIST_RECIPEOBJECT)
+ {
+ if (r.getName().toLowerCase().startsWith(letra.toLowerCase()))
+ {
+ // Hasta que no lleguemos al numero de items de la pagina, no se mostrara ninguno
+ cuentaRecipes++;
+ if (cuentaRecipes <= mostrarRecipesDesde)
+ {
+ continue;
+ }
+ if ((contador % 2) == 0)
+ {
+ html.append("<table bgcolor=131210><tr>");
+ }
+ else
+ {
+ html.append("<table bgcolor=202020><tr>");
+ }
+
+ String botonCompra = "<button value=\"" + r.getName() + "\" action=\"bypass -h npc_%objectId%_comprarRecipe_" + String.valueOf(r.getID()) + "\" width=206 height=32 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\">";
+ html.append("<td width=32>" + r.getIconRecipe() + "</td><td width=206><center>" + botonCompra + "</center></td><td width=32><center>" + r.getIconLevel() + "</center></td>");
+ html.append("</tr><tr>");
+ html.append("<td width=32>" + r.getIconProduction() + "</td><td><font color=\"LEVEL\">Precio: " + String.valueOf(r.getPrice()) + "</font> " + r.getNameCoin() + "</td><td>" + r.getIconCoin() + "</td>");
+ html.append("</tr><tr>");
+ html.append("</tr></table>");
+
+ contador++;
+ }
+ if (contador > (int) _limiteRecipesPorPagina)
+ {
+ break;
+ }
+ }
+
+ // Antes de terminar, mostrar tantos botones de navegacion como paginas haya( cantidad/_limiteRecipesPorPagina)
+ if ((contador > _limiteRecipesPorPagina) || (RecipeSeller_Handler.mostrarCantidadLetra(letra) > _limiteRecipesPorPagina))
+ {
+ int botones = (int) Math.ceil(RecipeSeller_Handler.mostrarCantidadLetra(letra) / _limiteRecipesPorPagina);
+ String botonesHtml = "<table><tr>";
+ int columna = 0, col_max = 5;
+ for (int x = 0; x < botones; x++)
+ {
+ botonesHtml += "<td><button value=\"" + String.valueOf(x) + "\" action=\"bypass -h npc_%objectId%_verRecipesAlfabeticamente_" + letra + "_" + String.valueOf(x) + "\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>";
+ columna++;
+ if ((columna % col_max) == 0)
+ {
+ botonesHtml += "</tr><tr>";
+ columna = 0;
+ }
+ }
+ botonesHtml += "</tr></table>";
+ html.append(botonesHtml);
+ }
+
+ html.append("</body></html>");
+
+ npcReply.setHtml(html.toString());
+ npcReply.replace("%objectId%", String.valueOf(activeChar.getTarget().getObjectId()));
+ activeChar.sendPacket(npcReply);
+ }
+
+ /**
+ * Esta metodo muestra las letras del alfabeto
+ * @param activeChar
+ */
+ private static void mostrarOrdenAlfabetico(L2PcInstance activeChar)
+ {
+ if (!(activeChar.getTarget() instanceof L2NpcInstance))
+ {
+ return;
+ }
+
+ NpcHtmlMessage npcReply = new NpcHtmlMessage(5);
+ TextBuilder html = new TextBuilder("<html><title>Recipe Seller</title><body>");
+
+ html.append("Vaya! Asi que tenemos delante a un profesional, ya sabes que recipe estas buscando?");
+ html.append("<table><tr>");
+ html.append("<td><button value=\"A\" action=\"bypass -h npc_%objectId%_verRecipes_A_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"B\" action=\"bypass -h npc_%objectId%_verRecipes_B_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"C\" action=\"bypass -h npc_%objectId%_verRecipes_C_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"D\" action=\"bypass -h npc_%objectId%_verRecipes_D_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"E\" action=\"bypass -h npc_%objectId%_verRecipes_E_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("</tr><tr>");
+ html.append("<td><button value=\"F\" action=\"bypass -h npc_%objectId%_verRecipes_F_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"G\" action=\"bypass -h npc_%objectId%_verRecipes_G_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"H\" action=\"bypass -h npc_%objectId%_verRecipes_H_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"I\" action=\"bypass -h npc_%objectId%_verRecipes_I_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"J\" action=\"bypass -h npc_%objectId%_verRecipes_J_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("</tr><tr>");
+ html.append("<td><button value=\"K\" action=\"bypass -h npc_%objectId%_verRecipes_K_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"L\" action=\"bypass -h npc_%objectId%_verRecipes_L_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"M\" action=\"bypass -h npc_%objectId%_verRecipes_M_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"N\" action=\"bypass -h npc_%objectId%_verRecipes_N_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"O\" action=\"bypass -h npc_%objectId%_verRecipes_O_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("</tr><tr>");
+ html.append("<td><button value=\"P\" action=\"bypass -h npc_%objectId%_verRecipes_P_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"Q\" action=\"bypass -h npc_%objectId%_verRecipes_Q_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"R\" action=\"bypass -h npc_%objectId%_verRecipes_R_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"S\" action=\"bypass -h npc_%objectId%_verRecipes_S_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"T\" action=\"bypass -h npc_%objectId%_verRecipes_T_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("</tr><tr>");
+ html.append("<td><button value=\"U\" action=\"bypass -h npc_%objectId%_verRecipes_U_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"V\" action=\"bypass -h npc_%objectId%_verRecipes_V_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"W\" action=\"bypass -h npc_%objectId%_verRecipes_W_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"X\" action=\"bypass -h npc_%objectId%_verRecipes_X_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("<td><button value=\"Y\" action=\"bypass -h npc_%objectId%_verRecipes_Y_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("</tr><tr>");
+ html.append("<td><button value=\"Z\" action=\"bypass -h npc_%objectId%_verRecipes_Z_0\" width=50 height=15 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td>");
+ html.append("</tr></table>");
+ html.append("</body></html>");
+
+ npcReply.setHtml(html.toString());
+ npcReply.replace("%objectId%", String.valueOf(activeChar.getTarget().getObjectId()));
+ activeChar.sendPacket(npcReply);
+ }
+}

Lo siguiente es una pequeña modificación en el Debes de estar registrado para poder ver el contenido indicado. Registrate o Conectate. Para tener una notificación de los recipes cargados.
Código (javascript) [Seleccionar]
Index: java/com/l2jserver/gameserver/GameServer.java
===================================================================
--- java/com/l2jserver/gameserver/GameServer.java (revision 6600)
+++ java/com/l2jserver/gameserver/GameServer.java (working copy)
@@ -33,6 +33,7 @@
 import org.mmocore.network.SelectorConfig;
 import org.mmocore.network.SelectorThread;
 
+import com.l2jrol.RecipeSeller_Handler;
 import com.l2jserver.Config;
 import com.l2jserver.L2DatabaseFactory;
 import com.l2jserver.Server;
@@ -478,6 +479,17 @@
  {
  _log.info(GameServer.class.getSimpleName() + ": Telnet server is currently disabled.");
  }
+
+ printSection("RecipeSeller");
+ try
+ {
+ RecipeSeller_Handler.inicializar();
+ }
+ catch (Exception e)
+ {
+ System.out.println("ERROR FATAL, no se pudo inicializar el RecipeSeller");
+ e.printStackTrace();
+ }
  }
 
  public static void printSection(String s)

Otra pequeña modificación en el L2Object, para registrar el nuevo type de NPC:
Código (javascript) [Seleccionar]
Index: java/com/l2jserver/gameserver/model/L2Object.java
===================================================================
--- java/com/l2jserver/gameserver/model/L2Object.java (revision 6600)
+++ java/com/l2jserver/gameserver/model/L2Object.java (working copy)
@@ -174,7 +174,10 @@
  L2NpcBufferInstance(L2Npc),
  L2TvTEventNpcInstance(L2Npc),
  L2WeddingManagerInstance(L2Npc),
- L2EventMobInstance(L2Npc);
+ L2EventMobInstance(L2Npc),
+ // L2ROL
+ L2NpcRecipeSeller(L2Npc);
 
  private final InstanceType _parent;
  private final long _typeL;

Otro nuevo fichero que tendrá que ser creado, el RecipeSeller_Handler. Se encarga de cargar las listas de recipes e iniciar el proceso de los objetos RecipeObject, que quedarán en la RAM para agilizar al máximo las tareas del vendedor de recipes:
Código (javascript) [Seleccionar]
Index: java/com/l2jrol/RecipeSeller_Handler.java
===================================================================
--- java/com/l2jrol/RecipeSeller_Handler.java (revision 0)
+++ java/com/l2jrol/RecipeSeller_Handler.java (working copy)
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2004-2014 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server 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.
+ *
+ * L2J Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jrol;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import javolution.util.FastList;
+
+import com.l2jserver.gameserver.datatables.RecipeData;
+import com.l2jserver.gameserver.model.L2RecipeList;
+
+/**
+ * @author Kimeraweb Esta clase inicializa los valores de las variables globales del RecipeSeller
+ */
+public class RecipeSeller_Handler
+{
+ public static Map<Integer, L2RecipeList> _LISTA_RECIPES;
+ public static FastList<RecipeObject> _FASTLIST_RECIPEOBJECT = new FastList<>();
+ private static double _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z;
+
+ public static Map<Integer, L2RecipeList> getRecipeList()
+ {
+ try
+ {
+ if (_LISTA_RECIPES.isEmpty())
+ {
+ inicializar();
+ // TODO Auto-generated constructor stub
+ }
+ }
+ catch (NullPointerException e)
+ {
+ inicializar();
+ }
+ return _LISTA_RECIPES;
+ }
+
+ public static void inicializar()
+ {
+ _LISTA_RECIPES = RecipeData.getInstance().getRecipes(); // Lista de recipes del juego
+ //
+ L2RecipeList recipe;
+ int recipeID = -1, craftLevel = -1, recipeRate = -1, recipeProduction = -1;
+ String recipeName = "Null";
+ Iterator<L2RecipeList> i = _LISTA_RECIPES.values().iterator();
+ System.out.println(_LISTA_RECIPES.size() + " recipes reconocidos de la lista de objetos.");
+
+ while (i.hasNext())
+ {
+ try
+ {
+ recipe = i.next();
+ recipeID = recipe.getRecipeId();
+ craftLevel = recipe.getLevel();
+ recipeName = getName(recipe.getRecipeName());
+ recipeRate = recipe.getSuccessRate();
+ recipeProduction = recipe.getItemId();
+ // _log.log(Level.INFO, "ID:" + recipeID + " Level:" + recipeLevel + " Name:" + recipeName + " Rate:" + recipeRate);
+ RecipeObject r = new RecipeObject(recipeID, craftLevel, recipeName, recipeRate, recipeProduction);
+ _FASTLIST_RECIPEOBJECT.add(r);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ System.out.println("ID:" + recipeID + " Lv:" + craftLevel + " Name:" + recipeName + " Rate:" + recipeRate);
+ }
+ }
+
+ for (RecipeObject r : RecipeSeller_Handler._FASTLIST_RECIPEOBJECT)
+ {
+ switch (r.getName().toLowerCase().substring(0, 1))
+ {
+ case ("a"):
+ _A++;
+ break;
+ case ("b"):
+ _B++;
+ break;
+ case ("c"):
+ _C++;
+ break;
+ case ("d"):
+ _D++;
+ break;
+ case ("e"):
+ _E++;
+ break;
+ case ("f"):
+ _F++;
+ break;
+ case ("g"):
+ _G++;
+ break;
+ case ("h"):
+ _H++;
+ break;
+ case ("i"):
+ _I++;
+ break;
+ case ("j"):
+ _J++;
+ break;
+ case ("k"):
+ _K++;
+ break;
+ case ("l"):
+ _L++;
+ break;
+ case ("m"):
+ _M++;
+ break;
+ case ("n"):
+ _N++;
+ break;
+ case ("o"):
+ _O++;
+ break;
+ case ("p"):
+ _P++;
+ break;
+ case ("q"):
+ _Q++;
+ break;
+ case ("r"):
+ _R++;
+ break;
+ case ("s"):
+ _S++;
+ break;
+ case ("t"):
+ _T++;
+ break;
+ case ("u"):
+ _U++;
+ break;
+ case ("v"):
+ _V++;
+ break;
+ case ("w"):
+ _W++;
+ break;
+ case ("x"):
+ _X++;
+ break;
+ case ("y"):
+ _Y++;
+ break;
+ case ("z"):
+ _Z++;
+ break;
+ default:
+ System.out.println("La letra " + (r.getName().toLowerCase()) + " no esta en la lista. Err:#55.");
+ break;
+ }
+ }
+
+ }
+
+ private static String getName(String name)
+ {
+ String newName = name.substring(3, name.length());
+ newName = newName.replace("_", " ");
+ return newName;
+ }
+
+ public static FastList<RecipeObject> getFastListRecipeObject()
+ {
+ return _FASTLIST_RECIPEOBJECT;
+ }
+
+ /**
+ * Este metodo devuelve un double con la cantidad de recetas que empiezan por una letra determinada
+ * @param letra
+ * @return
+ */
+ public static double mostrarCantidadLetra(String letra)
+ {
+ letra = letra.toLowerCase();
+ double d = 0.0;
+ switch (letra)
+ {
+ case ("a"):
+ d = _A;
+ break;
+ case ("b"):
+ d = _B;
+ break;
+ case ("c"):
+ d = _C;
+ break;
+ case ("d"):
+ d = _D;
+ break;
+ case ("e"):
+ d = _E;
+ break;
+ case ("f"):
+ d = _F;
+ break;
+ case ("g"):
+ d = _G;
+ break;
+ case ("h"):
+ d = _H;
+ break;
+ case ("i"):
+ d = _I;
+ break;
+ case ("j"):
+ d = _J;
+ break;
+ case ("k"):
+ d = _K;
+ break;
+ case ("l"):
+ d = _L;
+ break;
+ case ("m"):
+ d = _M;
+ break;
+ case ("n"):
+ d = _N;
+ break;
+ case ("o"):
+ d = _O;
+ break;
+ case ("p"):
+ d = _P;
+ break;
+ case ("q"):
+ d = _Q;
+ break;
+ case ("r"):
+ d = _R;
+ break;
+ case ("s"):
+ d = _S;
+ break;
+ case ("t"):
+ d = _T;
+ break;
+ case ("u"):
+ d = _U;
+ break;
+ case ("v"):
+ d = _V;
+ break;
+ case ("w"):
+ d = _W;
+ break;
+ case ("x"):
+ d = _X;
+ break;
+ case ("y"):
+ d = _Y;
+ break;
+ case ("z"):
+ d = _Z;
+ break;
+ default:
+ System.out.println("La letra " + letra + " no esta en la lista. Err#199");
+ break;
+ }
+
+ return d;
+ }
+}

Otro nuevo fichero es el L2NpcRecipeSellerInstance. Esta clase se encarga de que cuando cliques el NPC, muestre el HTML. Si quieres, aqui tambien podrias añadir una IA al NPC. Personalizarlo depende de ti :)
Código (javascript) [Seleccionar]
Index: java/com/l2jserver/gameserver/model/actor/instance/L2NpcRecipeSellerInstance.java
===================================================================
--- java/com/l2jserver/gameserver/model/actor/instance/L2NpcRecipeSellerInstance.java (revision 0)
+++ java/com/l2jserver/gameserver/model/actor/instance/L2NpcRecipeSellerInstance.java (working copy)
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2004-2014 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server 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.
+ *
+ * L2J Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.actor.instance;
+
+import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
+import com.l2jserver.gameserver.model.entity.RecipeSeller;
+
+/**
+ * @author Kimeraweb
+ */
+public class L2NpcRecipeSellerInstance extends L2NpcInstance
+{
+ // private static final Logger _log = Logger.getLogger("Lista Recipes");
+
+ /**
+ * @param objectId
+ * @param template
+ */
+ public L2NpcRecipeSellerInstance(int objectId, L2NpcTemplate template)
+ {
+ super(objectId, template);
+ setInstanceType(InstanceType.L2NpcRecipeSeller);
+ }
+
+ @Override
+ public void onSpawn()
+ {
+ super.onSpawn();
+ }
+
+ @Override
+ public String getHtmlPath(int npcId, int val)
+ {
+ String pom = "";
+
+ if (val == 0)
+ {
+ pom = "" + npcId;
+ }
+ else
+ {
+ pom = npcId + "-" + val;
+ }
+
+ return "data/html/l2recipeseller/" + pom + ".htm";
+ }
+
+ @Override
+ public void onBypassFeedback(L2PcInstance playerInstance, String command)
+ {
+ RecipeSeller.onBypass(command, playerInstance);
+ }
+
+ /*
+ * Quizas luego haga que haga cosas aleatorias con el PJ. Nueva IA
+ * @Override public void onAction(L2PcInstance player, boolean interact) { if (!canTarget(player)) { return; } }
+ */
+
+}

Y para terminar, una pequeña modificación en la clase RecipeData de L2J. Un método para obtener la coleccion de recipes cargados por el server. Sólo es un mapa de nombres de recipes y objetos necesarios para crear la receta. Es por eso que necesitabamos crear el objeto RecipeObject :)
Código (javascript) [Seleccionar]
Index: java/com/l2jserver/gameserver/datatables/RecipeData.java
===================================================================
--- java/com/l2jserver/gameserver/datatables/RecipeData.java (revision 6600)
+++ java/com/l2jserver/gameserver/datatables/RecipeData.java (working copy)
@@ -263,6 +263,15 @@
  }
 
  /**
+ * Se obtiene un MAP con la lista de recipes y los componentes para crear la receta. Custom Kimeraweb
+ * @return
+ */
+ public Map<Integer, L2RecipeList> getRecipes()
+ {
+ return _recipes;
+ }
+
+ /**
  * Gets the single instance of RecipeData.
  * @return single instance of RecipeData
  */
@@ -278,4 +287,5 @@
  {
  protected static final RecipeData _instance = new RecipeData();
  }
+
 }

El resultado se muestra a continuación.

Al clicar en el NPC, nos aparece el HTML creado a mano para el NPC:


Clicamos en el link (puedes mejorar el HTML, reconozco que está soso) y el proximo texto es la lista de letras del alfabeto. Solo hice esta para poder compartir mas temprano este share. Puedes crear otras, como mostrarlo por grados, por el nivel de crafteo que tenga el PJ...


Luego de elegir la letra, aparece la lista de recipes disponibles en la categoria. Los precios estan puestos acorde el precio del server ademas de unas modificaciones segun el cristal y nivel de crafteo necesarios. Estoy seguro de haber acertado con este criterio :)


Un mensaje de enhorabuena si la compra se ha realizado:


O un mensaje de enfurecimiento si pretendias comprar algo fuera de tus posibilidades:


Espero que ademas de que les guste, les haya servido como inspiración para crear sus propios tipos de NPC.

CitarPost Copiado de Kimeraweb.
Gracias por este gran aporte amigo ^^