Un nuevo type, el recipeSeller, para L2J H5 [Share]

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:

1
<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>
<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 Objeto, con funciones para que pueda desarrollar varios tipos de trabajo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
### 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>";
+   }
+   
+}
### 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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
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);
+   }
+}
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 GameServer.java. Para tener una notificación de los recipes cargados.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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)
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
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;
+   }
+}
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 :)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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; } }
+    */
+   
+}
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 :)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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();
    }
+   
 }
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();
 	}
+	
 }





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

Como hacer una Quest en L2J H5

Esta Quest es una demostración que sirve como iniciación a la programación en el server.

Si aun no tenéis habilidad manejando el core, el datapack es un buen sustituto, ya que L2J desde hace tiempo, unió el datapack al core de manera que obtenemos la ayuda de las clases de la misma manera que si estuviéramos en el core, es decir, al escribir el nombre de la clase (o de su instancia) y poner un punto, “Ejemplo L2Player.” Eclipse nos proporciona los métodos, como getLevel()getClan(), getInventory()… etc

En esta ocasión, se trata de devolver a un PJ que alcanzó el Lv85 al Lv1, pero añadiendole una nueva skill.

El proceso comienza creando el archivo java con un nombre descriptivo de la clase, en mi caso se adivina de que trata el archivo sin mirar el código.

Este archivo debe heredar la clase Quest, que posee los métodos para que nuestra quest funcione. Es decir, nos da todo el trabajo hecho, solo nos limitamos a escribir que queremos que pase segun las circunstancias.

Y cuales son esas circunstancias?

Estan descritas en el archivo “documentation.txt” o en la misma clase Quest. Cada método es descrito en detalle e incluso algunos de ellos aportan ejemplos, como OnAdvEvent().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
### Eclipse Workspace Patch 1.0
#P L2J_DataPack
Index: dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/Q1000_NuevaSkillPor85Levels.java
===================================================================
--- dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/Q1000_NuevaSkillPor85Levels.java  (revision 0)
+++ dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/Q1000_NuevaSkillPor85Levels.java  (working copy)
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2004-2014 L2J DataPack
+ * 
+ * This file is part of L2J DataPack.
+ * 
+ * L2J DataPack 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 DataPack 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 quests.Q1000_NuevaSkillPor85Levels;
+
+import com.l2jserver.gameserver.datatables.SkillTable;
+import com.l2jserver.gameserver.model.actor.L2Npc;
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.model.quest.Quest;
+import com.l2jserver.gameserver.model.quest.QuestState;
+import com.l2jserver.gameserver.model.quest.State;
+import com.l2jserver.gameserver.model.skills.L2Skill;
+
+/**
+ * @author Kimeraweb
+ */
+public class Q1000_NuevaSkillPor85Levels extends Quest
+{
+   
+   private final int _NPC_RENACENTISTA = 1000005, _MIN_LEVEL = 85;
+   L2Skill _skillAprendida;
+   
+   /**
+    * @param questId
+    * @param name
+    * @param descr
+    */
+   public Q1000_NuevaSkillPor85Levels(int questId, String name, String descr)
+   {
+       super(questId, name, descr);
+       
+       addStartNpc(_NPC_RENACENTISTA);
+       addTalkId(_NPC_RENACENTISTA);
+       // registerQuestItems();
+   }
+   
+   /**
+    * This function is called whenever a player clicks on a link in a quest dialog
+    */
+   @Override
+   public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
+   {
+       final QuestState st = player.getQuestState(getName());
+       if (st == null)
+       {
+           return null;
+       }
+       
+       String htmltext = null;
+       switch (event)
+       {
+       // Renacer a su riesgo
+           case "1000-02.htm":
+           {
+               if (player.getLevel() >= _MIN_LEVEL)
+               {
+                   st.startQuest();
+                   htmltext = "1000-03.html";
+                   _skillAprendida = SkillTable.getInstance().getInfo(1, 1); // Triple Slash
+                   player.setExp(1);
+                   player.addSkill(_skillAprendida);
+                   playSound(player, QuestSound.AMBSOUND_WINGFLAP);
+                   st.exitQuest(false, true);
+                   player.sendMessage("Has obtenido la skill " + _skillAprendida.getName());
+               }
+               break;
+           }
+           
+           default:
+           {
+               System.out.println("El evento " + event + " no esta registrado en Q1000");
+           }
+       }
+       return htmltext;
+   }
+   
+   /**
+    * This function is called whenever a player clicks to the "Quest" link of an NPC that is registered for the quest.
+    */
+   @Override
+   public String onTalk(L2Npc npc, L2PcInstance player)
+   {
+       QuestState st = player.getQuestState(Q1000_NuevaSkillPor85Levels.class.getSimpleName());
+       st.setState(State.CREATED);
+       
+       /*
+        * if (st == null) { return getNoQuestMsg(player); }
+        */
+       
+       String htmltext = "Ni idea de que hablas!!"; // getNoQuestMsg(player);
+       switch (st.getState())
+       {
+           case State.CREATED:
+           {
+               htmltext = (player.getLevel() < _MIN_LEVEL) ? "1000-01.html" : "1000-02.html";
+               break;
+           }
+           
+           case State.STARTED:
+           {
+               htmltext = "8)";
+               break;
+           }
+           
+           case State.COMPLETED:
+           {
+               htmltext = getAlreadyCompletedMsg(player);
+               break;
+           }
+       }
+       return htmltext;
+   }
+   
+   public static void main(String[] args)
+   {
+       new Q1000_NuevaSkillPor85Levels(1000, Q1000_NuevaSkillPor85Levels.class.getSimpleName(), "NuevaSkillPor85Levels");
+   }
+   
+}
### Eclipse Workspace Patch 1.0
#P L2J_DataPack
Index: dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/Q1000_NuevaSkillPor85Levels.java
===================================================================
--- dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/Q1000_NuevaSkillPor85Levels.java	(revision 0)
+++ dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/Q1000_NuevaSkillPor85Levels.java	(working copy)
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2004-2014 L2J DataPack
+ * 
+ * This file is part of L2J DataPack.
+ * 
+ * L2J DataPack 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 DataPack 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 quests.Q1000_NuevaSkillPor85Levels;
+
+import com.l2jserver.gameserver.datatables.SkillTable;
+import com.l2jserver.gameserver.model.actor.L2Npc;
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.model.quest.Quest;
+import com.l2jserver.gameserver.model.quest.QuestState;
+import com.l2jserver.gameserver.model.quest.State;
+import com.l2jserver.gameserver.model.skills.L2Skill;
+
+/**
+ * @author Kimeraweb
+ */
+public class Q1000_NuevaSkillPor85Levels extends Quest
+{
+	
+	private final int _NPC_RENACENTISTA = 1000005, _MIN_LEVEL = 85;
+	L2Skill _skillAprendida;
+	
+	/**
+	 * @param questId
+	 * @param name
+	 * @param descr
+	 */
+	public Q1000_NuevaSkillPor85Levels(int questId, String name, String descr)
+	{
+		super(questId, name, descr);
+		
+		addStartNpc(_NPC_RENACENTISTA);
+		addTalkId(_NPC_RENACENTISTA);
+		// registerQuestItems();
+	}
+	
+	/**
+	 * This function is called whenever a player clicks on a link in a quest dialog
+	 */
+	@Override
+	public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
+	{
+		final QuestState st = player.getQuestState(getName());
+		if (st == null)
+		{
+			return null;
+		}
+		
+		String htmltext = null;
+		switch (event)
+		{
+		// Renacer a su riesgo
+			case "1000-02.htm":
+			{
+				if (player.getLevel() >= _MIN_LEVEL)
+				{
+					st.startQuest();
+					htmltext = "1000-03.html";
+					_skillAprendida = SkillTable.getInstance().getInfo(1, 1); // Triple Slash
+					player.setExp(1);
+					player.addSkill(_skillAprendida);
+					playSound(player, QuestSound.AMBSOUND_WINGFLAP);
+					st.exitQuest(false, true);
+					player.sendMessage("Has obtenido la skill " + _skillAprendida.getName());
+				}
+				break;
+			}
+			
+			default:
+			{
+				System.out.println("El evento " + event + " no esta registrado en Q1000");
+			}
+		}
+		return htmltext;
+	}
+	
+	/**
+	 * This function is called whenever a player clicks to the "Quest" link of an NPC that is registered for the quest.
+	 */
+	@Override
+	public String onTalk(L2Npc npc, L2PcInstance player)
+	{
+		QuestState st = player.getQuestState(Q1000_NuevaSkillPor85Levels.class.getSimpleName());
+		st.setState(State.CREATED);
+		
+		/*
+		 * if (st == null) { return getNoQuestMsg(player); }
+		 */
+		
+		String htmltext = "Ni idea de que hablas!!"; // getNoQuestMsg(player);
+		switch (st.getState())
+		{
+			case State.CREATED:
+			{
+				htmltext = (player.getLevel() < _MIN_LEVEL) ? "1000-01.html" : "1000-02.html";
+				break;
+			}
+			
+			case State.STARTED:
+			{
+				htmltext = "8)";
+				break;
+			}
+			
+			case State.COMPLETED:
+			{
+				htmltext = getAlreadyCompletedMsg(player);
+				break;
+			}
+		}
+		return htmltext;
+	}
+	
+	public static void main(String[] args)
+	{
+		new Q1000_NuevaSkillPor85Levels(1000, Q1000_NuevaSkillPor85Levels.class.getSimpleName(), "NuevaSkillPor85Levels");
+	}
+	
+}

Si os habeis fijado, htmltext contiene el nombre del archivo html que queremos cargar de la quest. No es magia. La clase Quest comprueba que el mensaje a devolver por htmltext (mejor dicho, por el método donde se encuentra) incluye la coletilla “.htm” o “.html” y de ser así, en lugar de mostrar un texto, busca ese archivo para mostrarlo como texto.

El problema es que si queremos sustituir parte del texto del HTML que hemos cargado, por ejemplo si queremos incluir el level mínimo de la quest usando el comodín %level% en el html

1
<p>No tienes el level %level% para hacer esta quest!</p>
<p>No tienes el level %level% para hacer esta quest!</p>

y desde nuestro archivo java pretendemos hacer una sustitución

1
htmltext = htmltext.replace("%level%", _levelMinimo);
htmltext = htmltext.replace("%level%", _levelMinimo);

no funcionará. Tendremos que hacer este apaño editando la clase Quest, en su método showResult(). En el cual podemos ver que ya existe una sustitución disponible, se trata de %playername%.

A continuación vienen los htmls, puros y simples.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Index: dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/1000-02.html
===================================================================
--- dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/1000-02.html (revision 0)
+++ dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/1000-02.html (working copy)
@@ -0,0 +1,13 @@
+<html><head><title>Renacer</title></head>
+ <body>
+ Ah ya veo... A si que quieres volver a renacer conservando tus poderes?<br>
+ Imagino que sabras que esto no es reversible, asi que voy a explicarte lo que sucedera si aceptas.<br><br>
+ Volveras a nacer! Pero conservaras todas tus facultades, tu memoria, tus recuerdos, y a cambio de tu experiencia vivida, obtendras un nuevo poder, una nueva habilidad.<br><br>
+ <font color="LEVEL">Te preguntas, en que consiste esa habilidad?</font><br>
+ Pues... debes descubrirla... en cada individuo es distinta. Podrias ser mas veloz, o ser capaz de producir armas, tener mas vida... la magia de los dioses es aun oscura para los mas sabios.<br>
+ Realmente, estas hablando con la persona equivocada. Solo soy un aprendiz, no puedo decirte lo que un erudito podria aclararte. Pero me temo que eso es imposible. No se dejan ver hace muchas decadas.<br>
+ <br>
+ <br>
+ <a action="bypass -h Quest Q1000_NuevaSkillPor85Levels 1000-02.htm">Renacer a mi propio riesgo</a>
+ </body>
+</html>
\ No newline at end of file
Index: dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/1000-02.html
===================================================================
--- dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/1000-02.html (revision 0)
+++ dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/1000-02.html (working copy)
@@ -0,0 +1,13 @@
+<html><head><title>Renacer</title></head>
+ <body>
+ Ah ya veo... A si que quieres volver a renacer conservando tus poderes?<br>
+ Imagino que sabras que esto no es reversible, asi que voy a explicarte lo que sucedera si aceptas.<br><br>
+ Volveras a nacer! Pero conservaras todas tus facultades, tu memoria, tus recuerdos, y a cambio de tu experiencia vivida, obtendras un nuevo poder, una nueva habilidad.<br><br>
+ <font color="LEVEL">Te preguntas, en que consiste esa habilidad?</font><br>
+ Pues... debes descubrirla... en cada individuo es distinta. Podrias ser mas veloz, o ser capaz de producir armas, tener mas vida... la magia de los dioses es aun oscura para los mas sabios.<br>
+ Realmente, estas hablando con la persona equivocada. Solo soy un aprendiz, no puedo decirte lo que un erudito podria aclararte. Pero me temo que eso es imposible. No se dejan ver hace muchas decadas.<br>
+ <br>
+ <br>
+ <a action="bypass -h Quest Q1000_NuevaSkillPor85Levels 1000-02.htm">Renacer a mi propio riesgo</a>
+ </body>
+</html>
\ No newline at end of file

Ojalá te hayas fijado en el bypass, 1000-02.htm y verás que esa página HTML no se encuentra en la carpeta. Sin embargo, si te fijas en el código Java, el método onAdvEvent() recoge esta información para procesarla. Ya habrás adivinado que 1000-02.htm es sólo la infomación mandada por el enlace desde la página HTML, es decir, si hubiera puesto PEPE, hubiera llegado PEPE al método. Esto es muy interesante porque podrias sustituir esa cadena por la ID del PJ y realizar cambios importantes sin tener que programar en el core :)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Index: dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/1000-03.html
===================================================================
--- dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/1000-03.html (revision 0)
+++ dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/1000-03.html (working copy)
@@ -0,0 +1 @@
+<html><head><title>Renacer</title></head><body>Bienvenido...</body></html>
\ No newline at end of file
Index: dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/1000-01.html
===================================================================
--- dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/1000-01.html (revision 0)
+++ dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/1000-01.html (working copy)
@@ -0,0 +1,10 @@
+<html>
+ <head><title>Renacer</title></head>
+ <body>
+ A si que quieres renacer con una nueva habilidad, eh?<br>
+ Veamos... sabes? Veo que tienes potencial, carisma, valor... pero careces de la experiencia necesaria.<br>
+ <br>
+ No me malinterpretes, pero este conjuro requiere de un individuo experimentado. Podrias regresar sin nada, entiendes?<br>
+ Vuelve a verme cuando hayas alcanzado el nivel <font color="LEVEL">85</font>.
+ </body>
+</html>
\ No newline at end of file
Index: dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/1000-03.html
===================================================================
--- dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/1000-03.html (revision 0)
+++ dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/1000-03.html (working copy)
@@ -0,0 +1 @@
+<html><head><title>Renacer</title></head><body>Bienvenido...</body></html>
\ No newline at end of file
Index: dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/1000-01.html
===================================================================
--- dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/1000-01.html (revision 0)
+++ dist/game/data/scripts/quests/Q1000_NuevaSkillPor85Levels/1000-01.html (working copy)
@@ -0,0 +1,10 @@
+<html>
+ <head><title>Renacer</title></head>
+ <body>
+ A si que quieres renacer con una nueva habilidad, eh?<br>
+ Veamos... sabes? Veo que tienes potencial, carisma, valor... pero careces de la experiencia necesaria.<br>
+ <br>
+ No me malinterpretes, pero este conjuro requiere de un individuo experimentado. Podrias regresar sin nada, entiendes?<br>
+ Vuelve a verme cuando hayas alcanzado el nivel <font color="LEVEL">85</font>.
+ </body>
+</html>
\ No newline at end of file

Una vez hecho esto, y sin olvidar de que vuestro NPC debe iniciar la Quest con el siguiente bypass:

1
<button value="Quest" action="bypass -h npc_%objectId%_Quest" width=50 height=15 back="L2UI_ct1.button_df" fore="L2UI_ct1.button_df">
<button value="Quest" action="bypass -h npc_%objectId%_Quest" width=50 height=15 back="L2UI_ct1.button_df" fore="L2UI_ct1.button_df">

teneis que añadir el nombre de la quest y su ruta en script.cfg

1
quests/Q1000_NuevaSkillPor85Levels/Q1000_NuevaSkillPor85Levels.java
quests/Q1000_NuevaSkillPor85Levels/Q1000_NuevaSkillPor85Levels.java

Una advertencia. Si se os ocurriera hacer una carpeta para separar vuestras quests custom de las quests del server, teneis que saber las htmls creadas para vuestras quests serán buscadas en la ruta con el nombre de la quest, es decir, si pretendeis hacer:
quests/Mis Quests Customs/Q1000_NuevaSkillPor85Levels/1000-01.html
en realidad, los npcs buscaran las htmls en esta ruta:
quests/Q1000_NuevaSkillPor85Levels/1000-01.html

es decir, la carpeta con el nombre de la quest.

Espero que os haya servido para iluminaros de ideas y hacer vuestros servers mas interesantes, en lugar de copias retail editando los rates y pegando NPCs que ya existen en otros servers. Cread vuestra propia aventura!

Instanciando y detectando colisiones en Unity 3D.

http://kimeraweb.es/unity4/11/

En el ejemplo he creado el movimiento del cubo con:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using UnityEngine;
using System.Collections;
 
public class Move : MonoBehaviour {
 
    // Use this for initialization
    void Start () {
 
    }
 
    // Update is called once per frame
    void Update () {
        if (Input.GetKey(KeyCode.D))
            transform.Rotate(Vector3.up * Time.deltaTime * 40);
        if (Input.GetKey (KeyCode.A))
            transform.Rotate(Vector3.down * Time.deltaTime * 40);
        if (Input.GetKey(KeyCode.S))
            transform.Translate(Vector3.forward * Time.deltaTime * 5);
        if (Input.GetKey(KeyCode.W))
            transform.Translate(Vector3.back * Time.deltaTime * 5);
 
    }
}
using UnityEngine;
using System.Collections;

public class Move : MonoBehaviour {

	// Use this for initialization
	void Start () {

	}

	// Update is called once per frame
	void Update () {
		if (Input.GetKey(KeyCode.D))
			transform.Rotate(Vector3.up * Time.deltaTime * 40);
		if (Input.GetKey (KeyCode.A))
			transform.Rotate(Vector3.down * Time.deltaTime * 40);
		if (Input.GetKey(KeyCode.S))
			transform.Translate(Vector3.forward * Time.deltaTime * 5);
		if (Input.GetKey(KeyCode.W))
			transform.Translate(Vector3.back * Time.deltaTime * 5);

	}
}

Un script para mover la cámara:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using UnityEngine;
using System.Collections;
 
public class CamaraMove : MonoBehaviour {
    public float horizontalSpeed = 2.0f;
    public float verticalSpeed = 2.0f;
 
    // Use this for initialization
    void Start () {
 
    }
 
    // Update is called once per frame
    void Update () {
        float h = horizontalSpeed * Input.GetAxis("Mouse X");
        float v = verticalSpeed * Input.GetAxis("Mouse Y");
        transform.Rotate(v*Time.deltaTime, h*Time.deltaTime, 0);
 
    }
}
using UnityEngine;
using System.Collections;

public class CamaraMove : MonoBehaviour {
	public float horizontalSpeed = 2.0f;
	public float verticalSpeed = 2.0f;

	// Use this for initialization
	void Start () {

	}

	// Update is called once per frame
	void Update () {
		float h = horizontalSpeed * Input.GetAxis("Mouse X");
		float v = verticalSpeed * Input.GetAxis("Mouse Y");
		transform.Rotate(v*Time.deltaTime, h*Time.deltaTime, 0);

	}
}

Un script para disparar (instaciar):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using UnityEngine;
using System.Collections;
 
public class Fire : MonoBehaviour {
 
    public GameObject rocket;
    public GameObject player;
 
    // Use this for initialization
    void Start () {
 
    }
 
    // Update is called once per frame
    void Update () {
 
        if(Input.GetKey(KeyCode.Space) || Input.GetMouseButtonDown(0))
        {
            Disparar(rocket);
 
        }
 
    }
 
    private void Disparar(GameObject proyectil)
    {
        GameObject nuevoDisparo = new GameObject();
 
        nuevoDisparo = (GameObject)Instantiate(proyectil,
                                                player.transform.position, 
                                               player.transform.rotation) as GameObject;
 
        nuevoDisparo.AddComponent("AutoDestroy");
 
        nuevoDisparo.rigidbody.AddRelativeForce(new Vector3(0,10,player.transform.rotation.y),
                                                 ForceMode.VelocityChange);
    }
 
}
using UnityEngine;
using System.Collections;

public class Fire : MonoBehaviour {

	public GameObject rocket;
	public GameObject player;

	// Use this for initialization
	void Start () {

	}

	// Update is called once per frame
	void Update () {

		if(Input.GetKey(KeyCode.Space) || Input.GetMouseButtonDown(0))
		{
			Disparar(rocket);

		}

	}

	private void Disparar(GameObject proyectil)
	{
		GameObject nuevoDisparo = new GameObject();

		nuevoDisparo = (GameObject)Instantiate(proyectil,
												player.transform.position, 
		                                       player.transform.rotation) as GameObject;

		nuevoDisparo.AddComponent("AutoDestroy");

		nuevoDisparo.rigidbody.AddRelativeForce(new Vector3(0,10,player.transform.rotation.y),
		                                         ForceMode.VelocityChange);
	}

}

finalmente, un script para que cuando el jugador choque con el bolón, el bolón desaparezca e instancie un prefab de partículas.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
sing UnityEngine;
using System.Collections;
 
public class CollisionAndDestroy : MonoBehaviour {
 
    public GameObject explosion;
 
    // Use this for initialization
    void Start () {
 
    }
 
    // Update is called once per frame
    void Update () {
 
    }
 
    void OnCollisionEnter(Collision colision) {
        // Debug.Log("Colisionando con " + colision.gameObject.ToString());
        if (colision.gameObject.Equals(GameObject.Find("Player"))) {
 
            Instantiate(explosion,
                        this.transform.position,
                        this.transform.rotation);
 
            Destroy(this.gameObject);
        }
    }
 
    //void OnCollisionStay(Collision collision) {
        //foreach (ContactPoint contact in collision.contacts) {
            //print(contact.thisCollider.name + " hit " + contact.otherCollider.name);
            //Debug.DrawRay(contact.point, contact.normal, Color.white);
        //}
    //}
}
sing UnityEngine;
using System.Collections;

public class CollisionAndDestroy : MonoBehaviour {

	public GameObject explosion;

	// Use this for initialization
	void Start () {

	}

	// Update is called once per frame
	void Update () {

	}

	void OnCollisionEnter(Collision colision) {
		// Debug.Log("Colisionando con " + colision.gameObject.ToString());
		if (colision.gameObject.Equals(GameObject.Find("Player"))) {

			Instantiate(explosion,
			            this.transform.position,
			            this.transform.rotation);

			Destroy(this.gameObject);
		}
	}

	//void OnCollisionStay(Collision collision) {
		//foreach (ContactPoint contact in collision.contacts) {
			//print(contact.thisCollider.name + " hit " + contact.otherCollider.name);
			//Debug.DrawRay(contact.point, contact.normal, Color.white);
		//}
	//}
}

OnCollisionStay se puede usar también, pero este método se ejecuta continuamente mientras exista contacto entre los objetos.

Como editar los UKX del juego

Introducción

Bueno, tras bastante tiempo tratando con esto, preguntado a amigos más experimentados que yo en este campo y resolviendo mis propias dudas, y una vez que he cogido práctica y soltura, me he decido a hacer una guía para enseñaros a editar los ukx del juego. Os aviso que este es un trabajo meticuloso y requiere antención a todos los detalles para no meter la pata. Esta guía combinada con esta os servirá para crear cualquier cosa que podáis imaginar.

Antes de empezar con la guía, vamos a meter un poco de teoría:

Cualquier modelo del Lineage II (ya sea un arma, armadura, moob o cualquier cosa) consta de dos partes:

►  La textura en si misma, localizada en la carpeta systextures dentro de archivos con extensión .UTX

►  La mesh, es decir, la estructura a la que se amoldará la textura, localizadas dentro de la carpeta animations y con extensión .UKX

Una analogía de esto podría ser una casa, en la que diferenciamos una estructura de ladrillos (UKX), y un acabado exterior como podría ser la pintura (UTX).

¿Qué vamos a necesitar?

Como ya os he dicho, editar los UKX es un trabajo completamente diferente a editar los UTX, y por tanto se requieren programas totalmente diferentes. Aquí os dejo un pack con todo lo que vamos a necesitar:

Descargar

Este Pack contiene:

► Autodesk 3dsMax 2010 (Instalamos como lo haríamos con cualquier programa, leyendo los pasos de instalación para hacerlo correctamente)
► Plugin MD5 importer para 3dsmax (Una vez instalado el 3dsmax, extraemos en …\Autodesk\3ds Max 2009\Scripts)
► ZModeler 1.07b (extraer en una carpeta)
► OAUKX_C6 (para interlude) (extraer en una carpeta)
► Umodel (extraer en una carpeta)

Guía

Bueno, vamos a empezar por extraer todos los archivos en una carpeta común. Como he dicho mas arriba instalamos el 3dsmax 2010 y pegamos el plugin de MD5 en la carpeta scripts. El resto de programas quedan en nuestra carpeta:

81462679

37901730

Vamos a empezar por extraer los archivos dentro de los UKX.

En esta guía voy a crear una accesorio a partir de la máscara que lleva frintezza.

El UKX que contiene la mesh de frintezza se encuentra en la carpeta animations y se llama LineageMonsters3.ukx, por lo que voy a copiarlo en la carpeta del umodel. Ahora voy a crear un block de notas y escribiré lo siguiente:

1
umodel -export -md5 LineageMonsters3.ukx
umodel -export -md5 LineageMonsters3.ukx

► Donde LineageMonsters3.ukx sería el nombre de nuestro UKX

Bien, ahora vamos a guardar este archivo con extensión .bat (Archivo > Guardar Como):

84535167

Ahora ejecutamos y esperamos a que termine (puede tardar un poquillo), aunque aparezcan mensajes de WARNING simplemente son avisos:

69109018

Observamos que en la carpeta del umodel se ha creado una con el name de nuestro UKX y con dos carpetas dentro: [b]Meshanimation y SkeletalMesh[/b]. Bueno, ya tenemos nuestras MD5 listas para pasar por el taller: El 3dsMax 2010.

Bueno, abrimos el 3dsmax y vamos a la pestaña superior que pone Maxscript > Run Script y seleccionamos el MD5 Importer:

28545444

Se nos abrirá una nueva ventana en la que deseleccionaremos la opción See-Trought Mesh, presionaremos en Import MD5 y buscaremos la carpeta SkeletalMesh que habíamos conseguido con el umodel. Ahora seleccionamos una mesh (frintezza en mi caso):

77501094

Bueno aquí tenemos al esqueleto de Frintezza ^^ Ahora vamos a hacer dos pequeños ajustes:

► El primero, vamos a la pestaña superior Schematic View y seleccionamos todos los cuadraditos (llamemoslos así para no liar mas la cosa) menos el que está separado y damos a suprimir, cerramos ventana:

13745767

►Ahora vamos a el menú derecho y donde pone skin hacemos click secundario > suprimir (no necesario pero se trabaja mejor):

87985343

Ahora vamos a borrar las partes que queremos borrar de la mesh, para ello utilizaremos las diferentes herramientas de selección que tenemos en el menú derecho según nos convenga:

98307980

► NOTA: Yo voy a renderizar (colorear) la mesh para trabajar mejor y que el resultado final sea bueno, no obstante, es un paso innecesario y por eso no os voy a mostrar como se hace.

Aquí podéis ver como voy borrando las partes que no me interesan hasta dejar la máscara sola:

95772038

Una vez tenemos hemos eliminado todo lo que queríamos, vamos al menú superior > Export > Export, le ponemos un name y lo guardamos donde sea, recordando que tenemos que poner como formato .3DS, la opción de preserve Map Texture Coordinates la dejamos marcada.

14574810

Vale, ahora volvemos a nuestra carpeta donde teníamos todas nuestras herramientas y ejecutamos el zmodeler. Vamos a File > Import y seleccionamos nuestro archivo .3DS:

59899512

Ahora vamos a File > Export y lo guardamos en el escritorio por ejemplo con extensión .3ds (Daos cuenta de que antes la extesión era .3DS)

Bien, volvemos a abrir el 3dsmax e importamos nuestro .3ds:

65535266

Ya falta poco, ahora tenemos que añadirle el UVW, asique vamos al menú derecho, desplegamos el menú modifier list y seleccionamos UNWRAP UVW. Ahora volvemos a export otra vez y lo guardamos con extensión .OBJ

66208000

57781253

Se nos abre una ventana con configuraciones, no se que hacen todas exactamente, pero tras varias pruebas, yo las tengo así:

configsz

Hacemos click en export y luego en done. Vamos al directorio donde guardamos nuestro .OBJ y lo copiamos en la carpeta del OAUKX. Ahora arrastramos el .obj sobre el OAUKX y  nos pedirá que pongamos un número entre 1 y 14, esto hace pequeños ajustes dependiendo la raza y sexo del personaje, pero he oído que la diferencia es tan mínima que siempre se usa 1, por lo que escribimos 1 y damos a enter. Esto nos genera un archivo con extensión .ukx, que pegaremos en la carpeta animations.

47711406

Bueno bueno, ya tenemos lo complicado hecho. Ahora toca la parte del system. Vamos a abrir con el fileedit el armorgrp.dat y vamos a copiar la línea de cualquier accesorio (el del romantic chapeau por ejemplo):

Línea del Romantic Chapeau:

1
1 8565 0 3 6 5 0 Dropitems.drop_romantic_chaperon_m003_a LineageAccessoryTex.romantic_chaperon_m003_a_t00 icon.accessory_romantic_chaperon_i00 4294967295 10 13 0 0 19 1 LineageAccessory.Mfighter_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Ffighter_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Mdarkelf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Fdarkelf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Mdwarf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Fdwarf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Melf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Felf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Mmagic_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Fmagic_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Morc_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Forc_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Mshaman_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Fshaman_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 0 0 0 0 1 LineageAccessory.Mfighter_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 LineageEffect.p_u002_a 1 ItemSound.itemdrop_armor_glove ItemSound.itemequip_armor_cloak 1 0 0 0 0 0 0 0
1 8565 0 3 6 5 0 Dropitems.drop_romantic_chaperon_m003_a LineageAccessoryTex.romantic_chaperon_m003_a_t00 icon.accessory_romantic_chaperon_i00 4294967295 10 13 0 0 19 1 LineageAccessory.Mfighter_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Ffighter_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Mdarkelf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Fdarkelf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Mdwarf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Fdwarf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Melf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Felf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Mmagic_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Fmagic_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Morc_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Forc_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Mshaman_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Fshaman_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 0 0 0 0 1 LineageAccessory.Mfighter_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 LineageEffect.p_u002_a 1 ItemSound.itemdrop_armor_glove ItemSound.itemequip_armor_cloak 1 0 0 0 0 0 0 0

Línea de mi accesorio:

1
1 9999 0 3 6 5 0 Dropitems.drop_romantic_chaperon_m003_a LineageMonstersTex3.frintessa_t01 icon.accessory_romantic_chaperon_i00 4294967295 10 13 0 0 19 1 frintezza.frintezza 1 LineageMonstersTex3.frintessa_t01 1 1 1 LineageAccessory.Ffighter_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Mdarkelf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Fdarkelf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Mdwarf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Fdwarf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Melf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Felf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Mmagic_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Fmagic_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Morc_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Forc_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Mshaman_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Fshaman_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 0 0 0 0 1 LineageAccessory.Mfighter_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 LineageEffect.p_u002_a 1 ItemSound.itemdrop_armor_glove ItemSound.itemequip_armor_cloak 1 0 0 0 0 0 0 0
1 9999 0 3 6 5 0 Dropitems.drop_romantic_chaperon_m003_a LineageMonstersTex3.frintessa_t01 icon.accessory_romantic_chaperon_i00 4294967295 10 13 0 0 19 1 frintezza.frintezza 1 LineageMonstersTex3.frintessa_t01 1 1 1 LineageAccessory.Ffighter_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Mdarkelf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Fdarkelf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Mdwarf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Fdwarf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Melf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Felf_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Mmagic_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Fmagic_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Morc_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Forc_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Mshaman_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 1 LineageAccessory.Fshaman_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 1 1 0 0 0 0 1 LineageAccessory.Mfighter_romantic_chaperon_m003_a 1 LineageAccessoryTex.romantic_chaperon_m003_a_t00 LineageEffect.p_u002_a 1 ItemSound.itemdrop_armor_glove ItemSound.itemequip_armor_cloak 1 0 0 0 0 0 0 0

¿Qué cambios he hecho?

►He cambiado la ID.
►El Dropitems.drop_romantic_chaperon_m003_a no lo he cambiado porque no he creado una mesh para ello, dado que esta guía es simplemente orientativa.
►He cambiado la textura del romantic chapeau por LineageMonstersTex3.frintessa_t01.
►He cambiado la mesh del romantic chapeau por frintezza.frintezza (el ukx que hemos creado y la mesh tienen el mismo nombre).
► Daos cuenta que solo he editado el armorgrp para que se vea en el Male Figther, tendríamos que cambiar todas las líneas para que se viese en todos los personajes.

Guardamos. También debemos de editar el itemname-e (copiáis la línea del Romantic Chapeau y la cambiáis el nombre y la ID).

Esto es todo, ahora veamos el resultado final:

shot00011fj

Créditos: by Buzzin

Quien ataco su email

Tu cuenta de correo ha sido atacada?

Ahora existe una herramienta para saberlo, se llama Have I been pwned?

Es importante saber si ha sido atacada porque muchos de nosotros, por estadística, usamos la misma contraseña para todos nuestros sitios.

Es importante que tu contraseña contenga caracteres alfanuméricos y símbolos, de esta manera se reduce drásticamente las posibilidades de estar siendo controlado.

Por experiencia, he sabido que mi cuenta de correo ha sido atacada por notificación de Yahoo, que me obligó a cambiar mi contraseña por otra mucho mas elaborada.

Como no olvidar tu nueva elaborada contraseña?

Lo mas habitual es sustituir las vocales por números, de tal manera:

EstaEsMiPass por €st4€sm1p4ss o incluso €5t4€5m1p455

Evitar usar números relacionados con vuestra vida, fecha de nacimiento, número de teléfono, número de identificación, etc… porque aunque se supone que tus datos son tratados con confidencialidad, seguramente puedes googlearte, o encontrarte en las páginas de información telefónica.

Como crear una contraseña que no se me olvide?

Spots publicitarios, marcas de bebidas, comida, etc… MeGustaLaPizza -> M3GUST@L4P1ZZ4, SOYPOETA -> S01P03T4, siempre quedaros con una regla para no olvidar si usaste mayúsculas o minúsculas. Por ejemplo, la primera siempre mayúscula, o, el tipo CamelCase, que consiste en unir las palabras poniendo en mayúsculas la siguente, o las pares mayúsculas y las impares mminúsculas o la primera, la segunda, la cuarta… (progresión geométrica) MIcOntrAseña -> M1c0ntr$s3ñ4 (la $ es el 4 mayúsculas).

Como averiguan mi contraseña?

Realmente no lo saben. Lo sacan mediante la fuerza bruta sumado a la tendencia por estadística, porque los intentos no son infinitos y tienen que lograrlo en un número determinado de veces.

Hay programas en la red denominados bots que se dedican a recoger datos aleatoriamente de la red y a clasificarlos. Cuando ese programa encuentra lazos que unen cierta información, entonces va creando un perfil que puede ser el tuyo. Tu nombre del padrón del ayuntamiento, tu teléfono de cuando te registraste en una cuenta, tu dirección de cuando compraste algo en internet… todo esto va siendo almacenado esperando encontrar datos comunes que hagan un perfil de tu persona. Si fueron capaces de reunir todos tus datos, pueden hacerse pasar por ti y encontrar que compraste una hermosa televisión de plasma de 3000 euros, como le pasó a un conocido, por no tener cuidado de tener su navegador limpio. Un malware recopiló todos sus datos cuando registró un servidor, y al mes siguiente le cargaron un plasma.

Y sin quererlo he añadido el último paso para andar seguros por la red, mantener nuestro navegador lejos de esas barras personalizadas, plugins y demás inventos de esos personajes que nos obligan a instalar sus herramientas para poder descargar algo de nuestro interés.

Para los neuróticos:

Existen páginas de correo anónimas, que son creadas con el único fin de que os envien vuestra contraseña sin tener que aguantar el spam del foro o de la web donde te has registrado.

Además existen otras páginas para navegar de forma anónima y segura mediante proxys, que ocultan vuestro rastro y vuestra presencia.

gface1

Warface, juego de guerra (FPS) online gratuito

Ya puedes disfrutar de la potencia del motor de Cry Engine jugando al Warfare en la web. Al igual que con Unity3D, instalas un pluging, el juego carga y ya estás listo para jugar.

La dirección para jugar: https://gface.com/

gface4

Mi primera experiencia con una tarjeta GT-630, y un dual core 2.8 ha sido justita para jugar a este juego online, además de inesperada. El juego cuenta con todos los detalles para disfrutar de un FPS (first person shooter) de gran calidad.

gface2

La presentación se queda corta para intuir que nos vamos a encontrar. Aunque recalco, que necesitas una máquina moderna para disfrutar de este juego.

gface3

 

Puedes jugar en varios continentes, aunque te hayas creado el personaje inicial en otro distinto.

gface5

 

Asi es como te verán los otros jugadores. Realmente se trata de un juego en 3D al estilo Call of Duty.

gface6

En el momento de crear la cuenta, tienes cinco misiones. Sólo podrás jugar a dos de ellas la primera vez.

gface7

Esta es tu vista, sosteniendo el arma y a los lados, los demás jugadores.

En este juego tienes variedad de armas y acciones, como correr, saltar, agacharse, trepar y el modo cooperativo para trepar por paredes muy altas, que sólo pueden ser sorteadas con ayuda de otro jugador.

Este juego es bastante recomendable, pero no es totalmente gratis si quieres actualizar tu arsenal. Aunque si estás pensando en probarlo, el juego no decepciona.

 Pros: Calidad de la música, imagen, sonido, gráficos, movimientos…

Contras: Tarda mucho en cargar la primera vez, necesita de un ordenador potente.

 

No puedo terminar el artículo sin agradecer a KingBorraxo69 que me pasase el enlace :)

Logo WWW

Propagación de tu DNS

Acabas de adquirir tu dominio, y te acaban de decir que esperes entre 24 y 48 horas… y como saberlo exactamente?

Existen webs con herramientas para tal fin, se las denominan DNS Propagation Checker.

Te dejo unas cuantas que funcionan muy bien:

http://www.intodns.com/

dns1

 

https://www.whatsmydns.net

Propagacion de DNS

 

 

http://viewdns.info/propagation/

Propagacion de DNS

 

Propagación DNS

http://www.mob.net/~ted/tools/dns.php3

Ideal si no tienes ancho de banda.

 

 

 

 

Con estas webs puedes verificar si los nodos han actualizado la dirección IP y apuntan a tu dominio, además de saber donde tu web no se ve.

 

Hospedaje web gratis con dominio!

Obtén tu web con 5GB gratis y dominio incluido.

Espacio web con dominio, gratis por un año.

Si, no hay truco. Abres una cuenta aqui, y te dan 5GB gratis mas un dominio que tu elijas sin limite de transferencia por un año. No tienes que pagar nada.

Como a caballo regalado no se le mira el diente, entré y lo probé. Es tan sencillo su uso que tuve que pedir asistencia para entender el menú. Estoy acostumbrado a los paneles de toda la vida, como Plesk

plesk

pero este otro panel está pensado para usuarios sin conocimientos informáticos.

haga_clic_en_dns_avanzadoLo tienes todo, y lo que yo estaba buscando, PHP y MySQL. Además la asistencia técnica es en español e inglés y son muy rápidos en atenderte. Un par de minutos :-)

He estado en otros hostings, y la asistencia técnica es muy deficiente. Recuerdo por ejemplo, BlueHost, catalogado como número uno en precio-servicio. Es lento, a veces sufre de caídas o lag, el servicio técnico es lento y sólo atienden en inglés.

Aquí sin embargo, tienen algo muy bueno. Puedes captar a tus amigos y ampliar tu espacio web inicial y renovarlo al precio básico. Es decir, por cada amigo que se suscriba, te dan 3GB, y si el año siguiente quieres renovar, el precio es el de 5GB, no te cobran el espacio extra que te has ganado.

Tu espacio para la base de datos es el restante de tu espacio web, es decir, que si tu web pesa 10 megas, tienes 4990 megas de base de datos disponible. Al contrario que en la mayoría de los otros hostings, donde te dan 200 megas a cambio de espacio ilimitado. Es un engaño, ya que si montas un blog o un foro, estas limitado a los 200 megas de tu base de datos.

La respuesta es instantánea. El ping es muy bajo y obtienes una señal excelente a ambos lados del charco. Tengo que añadir que los usuarios del continente americano me han dicho que la oferta no está disponible aún.

El acceso a la base de datos está limitada al administrador phpMyAdmin que proveen, no puedes acceder desde tu ordenador directamente con administradores al estilo de Navicat.

Proveen de fsockopen para todos los puertos. Otros hosts solo te permiten acceso a unos pocos puertos. Asi que tambien se convierte en ideal para montar tu servidor de L2J, pudiendo mostrar la información de tu servidor online. 

Además, esto viene genial cuando empiezas con tu servidor, normalmente de poco presupuesto. Si en un año no has logrado tu meta, no renuevas el dominio y no tienes que pagar.

No lo olvides, entras con este enlace y asi me gano 3GB mas de espacio (gracias!) que no repercuten en tu servicio, obtendrás tus 5GB + el dominio que tu quieras gratis durante un año.