Jump to content

Search the Community

Showing results for tags 'metin2'.



More search options

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • Metin2 Zone
    • Community
    • Presentations and farewells
  • Private Servers
    • Server Presentations
    • Project showcase
  • General
    • General Discussions
    • Partnerships and Cooperation
    • Services and trading
    • Helps and questions
    • Reviews and advice
    • Offtopic
  • Technical
    • Programming
    • Metin2 Server Modding
    • Web Development
    • Security
    • Operating Systems
    • Computing
  • Art
    • Design and graphic section
    • Gallery of images and videos
    • 3D Modelling
    • Mapping
  • Downloads
    • Client and Server files
    • 3D Models
    • Metin2 Images
    • Maps
    • Translation
  • Archive
    • Offline Servers
    • Temas Links Caidos

Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


Email


Sitio web


Jabber


Skype


Discord


Location


Intereses

Found 244 results

  1. Hola a todos En esta ocasión voy a explicarles con pleno detalle cómo hice el editor de ítems. Aclaro que la versión que tengo posteada es antigua en cuanto a código, y la versión que les voy a enseñar ahora es basada en la experiencia que he adquirido luego. Esto quiere decir, código limpio, modular, más desacoplado, más cohesivo, mejores nombres de variables, todo en inglés, mejor estructurado, más dinámico, más fácil de leer y modificar, más actualizado en cuanto a tipos de objetos y bonus, convertido en librería, mejor dicho, toda una obra de arte. Verán la forma de programación más profesional que jamás hayan visto, y lo mejor de todo, esto es gratis. Esta quest no fue hecha una vez en tres días, sino fue la experiencia de una persona que lleva más de 5000 horas de programación en lua y quests, con una carrera profesional en este tipo de temas y varios años de conocimiento sobre el juego y su software. Esto va a ser extenso pero a la vez va a estar muy enriquecido. Les cuento que la primera vez que la empecé a hacer, no sabía cómo hacerla, solo empecé. ¿Alguna vez les ha pasado? lo importante es escribir la primera línea de código, nunca te quedes pensando cómo hacer algo o nunca lo harás; este es mi primer consejo. Cuando empecé a hacerla, ya había creado el bloque quest, state y empezaba mi primer when, fue el npc donde iba a hacer el take. Con mi experiencia en quest pude saber hacia dónde me tenía que dirigir luego. Aquí vamos. Primero que todo debemos saber cómo es que se supone que debería funcionar. Obviamente ustedes ya la vieron y la metieron en su sv. Pero pónganse en mis zapatos, que yo solo tenía una idea en la cabeza, no tenía ninguna quest para guiarme. ¿Cómo es que se hace para llevar una idea a que se haga real? Lo único que pensé fue, bueno, quiero que algo agarre cierto objeto equipable que se le pueda meter bonus, me muestre los bonus y valores que pueda elegir y ya, cuando mire el objeto ya tenga esos bonus puestos tal cual el orden en que los elegí. Esto lo vi posible porque sé el alcance que tienen las quests, aunque sean limitadas. En este punto sabía que un ítem se puede seleccionar cuando "algo" agarre el id de él (ojo, el id, no el vnum). La diferencia entre el id de un objeto y el vnum, es que el id representa la identidad de un objeto, y ese id es único en cada objeto. Mientras que el vnum es la "plantilla" de ese objeto, de manera que cada vez que crees un objeto con el vnum, estarás sacando el objeto de la plantilla de los ítems que es el item_proto. Proto significa base. Los objetos ya creados aparecen en la tabla player.item. Sigo... Una vez un NPC captura el id de un objeto con el disparador take (los login, logout, take, .chat, unmount, etc son triggers pero traducidos son disparadores), ya puedo modificar todos sus atributos que están en las columnas de la tabla player.item. Con la tabla player.item_proto puedo saber si el objeto es un arma o una armadura, y si tiene media o no; ya que ese atributo está desde la base. Para saber esto debes haber estudiado muy bien el item_proto ya que es lo más básico en el desarrollo de servidores. También en la tabla player.item_proto puedo ver los bonus base que el objeto ya tiene, para no tener que repetirlos. Otra cosa de las quests, es que tienen unas funciones listadas en quest/quest_functions. Ahí hay muchas que son útiles para modificar atributos del ítem y lo hace desde el source, o sea que la modificación se hace de inmediato. Las vamos a ver más adelante. Luego necesito mostrar los bonus que están disponibles para ese objeto, que son los mismos bonus que me salen cuando cambio los bonus. Estos bonus están en player.item_attr. Esto también es básico cuando estás aprendiendo estos temas. Aquí se complica un poco las cosas... pero, les digo de una vez que no pensé en nada de eso cuando empecé a hacer la quest. Iniciemos Esta quest será modular, es decir, utilizaré funciones que se conectarán las unas con las otras para lograr nuestro resultado. Es la mejor manera que he encontrado para que cualquier otra persona que vea la quest pueda entenderla muy rápido. item_editor_camilo.quest quest item_editor_camilo begin state start begin when 11000.take or 11002.take or 11004.take begin item_editor_camilo_lib.startItemEditor() end end end Si no entiendes lo de los bloques o no tienes un entorno preparado para programar quests, lee y haz los pasos de este post: "cuando" 11000 (guardián de la ciudad) "tome" "empieza" Ahí estamos diciendo literalmente que cuando el NPC de vnum 11000, 11002 o 11004 tome un objeto (es arrastrado hacia él), entonces empiece a ejecutar lo que viene abajo y "termine" en donde dice end. item_editor_camilo_lib.startItemEditor() En esta instrucción estamos llamando a una función de una tabla que no hemos creado. Vamos a crearla. item_editor_camilo_lib.lua item_editor_camilo_lib = {} item_editor_camilo_lib.startItemEditor = function() chat("Esto debería mostrar cuando ponga un objeto.") end Para esto crearás un archivo llamado item_editor_camilo_lib.lua. En verdad el nombre no importa pero la extensión sí, no la cambies, tiene que ser lua. Un archivo es .quest cuando el código empieza por quest [nombre] begin. Un archivo es .lua cuando no empiece por quest. En este caso no empieza por quest. Este archivo por el momento no tiene conexión con nada. Nuestro archivo padre se llama questlib.lua que ya está configurado desde la fuente para que sea la librería papá. Allí se pone todo tipo de código suelto, funciones, inicializar variables, llamar a otros archivos, etc. Conectaremos el questlib.lua con nuestra librería que acabamos de crear. Para ello ponemos esta línea en el questlib.lua: dofile(get_locale_base_path().."/quest/system/_item_editor_camilo/item_editor_camilo_lib.lua") Esta instrucción si la traduces al español lo dice todo pero igual te lo voy a explicar. Aquí el questlib.lua se extiende hacia ese archivo que declaras en esa ruta, por lo tanto, lo ejecuta y lo tendrá en cuenta cuando llamen a alguna función o variable que esté declarada allí. Ese get_locale_base_path() devuelve locale/xxxx. En xxxx va el country o país de tus files, sean turkey, germany, spanish, etc. Ese get_locale_base_path() no lo toques, solo modifica lo que está en verde. Y si no te funciona así, pon la ruta completa desde el /usr. Lo más probable es que la ruta que tengas no sea la misma mía pero sí te recomiendo que sigas el mismo patrón que las otras quests. Por ejemplo, si en tu carpeta quest hay una carpeta system o source donde meten todas las quests, métela ahí. Si en tu carpeta quest ponen las quest en carpetas que empiezan en "_", crea una carpeta nueva con el mismo patrón. Te recomiendo crear una carpeta donde estará el editor, no combines esta quest y librería con varios archivos de otras cosas. En ese archivo lib que creamos he hecho las funciones de una de las maneras; hay otra manera, como están en questlib.lua pero nosotros somos más sofisticados y las hemos guardado en una tabla. Prueba la quest y revisa si en el juego te funciona para ver si todo está bien. Si te funciona, sigamos. Vamos a hacer que el NPC determine si el objeto tiene media y habilidad. La verdad no nos importa si es arma, armadura, acc, etc. item_editor_camilo_lib.startItemEditor = function() local _, item_table = mysql_direct_query(string.format("SELECT addon_type FROM player.item_proto WHERE vnum = %s;", item.vnum)) if tonumber(item_table[1].addon_type) == -1 then --aquí haríamos lo que haríamos si el objeto tuviera media else syschat("Este objeto no se puede editar.") end end Un momento... ¿Qué acabo de hacer? Bueno, primero que todo vamos a tener que usar la maravillosa función de mysql_direct_query que les dejaré como adjunto al final de este post para que la metan en el src. La saqué de [Hidden Content] Solo hay que meterla y ya, ya les explicaré cómo funciona. Uso del string.format: resulta que cuando creamos los chat o los say o derivados textos que se muestran en el personaje, ponemos variables entre esos textos y no tienen un formato fácil de leer. A mí me gusta mucho usar el string.format. Sin string.format: chat("Soy "..pc.get_name().." y soy nivel "..pc.level) --Esto muestra: --Soy Camilo y soy nivel 120 Con string.format: chat(string.format("Soy %s y soy nivel %s", pc.get_name(), pc.level)) --Esto muestra: --Soy Camilo y soy nivel 120 Son dos formas de crear un string (cadena de texto) pero una es más fácil de leer y modificar que la otra. Ese %s es el que determina la variable, y esa "s" se refiere a que es string. La verdad hay un %d que se refiere a double (números decimales) pero no la uso porque finalmente así sea número va a mezclarse con un string y todo será un string. Así que para todas será siempre %s. Por si te enredas con ese código, míralo de esta manera: local texto_presentacion = string.format("Soy %s y soy nivel %s", pc.get_name(), pc.level) chat(texto_presentacion) --Esto muestra: --Soy Camilo y soy nivel 120 Así se entiende mejor, ¿verdad? Mira ese item.vnum, te preguntarás que por qué item.vnum y no item.get_vnum(). La respuesta está en el questlib.lua. Hay tablas donde asignan nuevos valores a estas funciones, miren: Así que si pones por ejemplo item.type, será lo mismo que poner item.get_type(). Por qué funciona eso así? por unas estructuras llamadas metatables. Lo mismo funciona para algunas funciones de pc y npc. Solo debes revisar. Ahora vamos por entender la query. Query significa consulta, refiriéndonos a la base de datos. Una "búsqueda" en la base de datos. Hay un lenguaje llamado SQL que también se estudia y todo, pero es más básico que los lenguajes que conocemos. SELECT addon_type FROM player.item_proto WHERE vnum = %s; Aquí le estoy diciendo en español "seleccione" addon_type [columna] "de" player.item_proto[tabla] "donde" vnum = xxxx; No es el objetivo enseñar SQL porque hay tutoriales en internet pero haré lo posible para explicar de pasada y quede algo en la mente. Ese player.item_proto lo pongo porque necesito seleccionar una base de datos. Recuerden que las bases de datos son account, common, information_schema, log, mysql, etc. Es decir, las que aparecen debajo de tu conexión. El mismo ícono indica una base de datos. La forma sería base_de_datos.nombre_tabla El resultado de esta consulta obviamente será un solo registro o resultado porque solo hay un vnum único en item_proto. Ya sabemos el tipo de resultado que puede arrojar esa consulta. Ahora si el número el diferente de -1 pues deducimos que no es un arma de media y listo. local _, item_table = mysql_direct_query(... ¿Qué significa ese "_" y por qué hay dos variables que se asignan luego de usar la función mysql_direct_query? Si ven la función por source, verán que retorna dos variables. Dada la estructura de la tabla que retorna, no se puede obtener la cantidad de registros usando el típico table.getn(). La primera variable es la cantidad de registros obtenidos de la consulta, y la segunda variable es la tabla obtenida de la consulta. Puse un "_" porque no voy a utilizar la cantidad de registros en ninguna parte. Es obligatorio poner las dos variables o sino habría un problema de redundancia. La estructura de las tablas obtenidas de mysql_direct_query es: nombre_tabla[num_registro][nombre_columna] Las típicas funciones de mysql_query tienen otra estructura. Entonces para obtener el -1 debemos hacer esto: item_table[1].addon_type --o que es lo mismo: item_table[1]['addon_type'] Explicación de esa forma de acceder al dato: Resulta que si tienes una variable que no tiene espacios puedes hacer como en la primera opción. Pero si tienes una variable con espacios, no puedes hacer esto: item_table[1].addon type --error En cambio sí puedes hacer esto: item_table[1]['addon type'] --aunque obviamente en las columnas de la db no pueden haber espacios Aclaro que solo en dado caso de que la variable tenga espacios o uses variables dentro de ella, que ya lo veremos más adelante. Cuando recibimos ese -1 puede ser un -1 propiamente, o puede ser un "-1". Me ha pasado de las dos formas. Así que es mejor asegurarnos. Hay que poner el tonumber() así como está en el código, para garantizar que estamos comparando con un número. No es lo mismo "-1" que -1. El primero es un string y el segundo es un number, no se pueden comparar. Como opcional ponemos que en dado caso que no sea un objeto con media o habilidad, le decimos que no se puede editar el objeto. Pasemos a la siguiente función: item_editor_camilo_lib.startItemEditor = function() local _, item_table = mysql_direct_query(string.format("SELECT addon_type FROM player.item_proto WHERE vnum = %s;", item.vnum)) if tonumber(item_table[1].addon_type) == -1 then --aquí haríamos lo que haríamos si el objeto tuviera media item_editor_camilo_lib.switchingMeanSkill() else syschat("Este objeto no se puede editar.") end end Para llamar a otra función hay que seleccionar la tabla y luego llamar a la función con el punto. item_editor_camilo_lib.switchingMeanSkill() switchingMeanSkill significa cambiar media y habilidad. Vamos a crearla: item_editor_camilo_lib.switchingMeanSkill = function() say_title_center("Editor de ítems By Camilo[ENTER]") say_item_vnum(item.vnum) say("Ingresa la media que quieres sacar") local mean_chosen = tonumber(input()) if mean_chosen == nil or mean_chosen < 0 or math.mod(mean_chosen, 1) != 0 then return end say_title_center("Editor de ítems By Camilo[ENTER]") say_item_vnum(item.vnum) say("Ingresa la habilidad que quieres sacar") local skill_chosen = tonumber(input()) if skill_chosen == nil or skill_chosen < 0 or math.mod(skill_chosen, 1) != 0 then return end end Le vamos a mostrar al personaje el menú cuando le pongamos el objeto de media. Decoramos con say_title_center. Si no tienes esa función, dejo todo adjunto al final. También decoramos con say_item_vnum poniendo el ícono del objeto en el menú. Acá le juego a un método: la persona selecciona un valor de media y un valor de habilidad, y el primero que caiga mayor o igual, se queda. Voy a explicar esto muy bien porque vi que muchas personas no lo entendieron. Si yo pongo media 50 y habilidad 25, cuando caiga 25 de habilidad se va a detener ahí. Si cae 26 o 27 de habilidad también se detiene ahí. Si cae 52 de media primero que 25 de habilidad, pues quedan esos 52 de media y se detiene el dopador. Nota: en la parte de los bonus de la media y habilidad es un dopador, no es un editor, ya que estamos haciendo cambios realmente. Le pediremos a la persona que digite un número. Esto se hace con input() y lo convertimos a número con tonumber(). Hacemos la validación. La validación es un filtro. Si el texto entregado por la persona no nos sirve en nuestras operaciones, no debemos dejarlo pasar. Si la persona dejó en blanco el espacio ponemos: if mean_chosen == nil Si la persona puso media negativa tampoco lo dejemos pasar. Y si la persona puso números decimales tampoco lo dejemos pasar; yo lo hago con math.mod, explico: Hay una operación matemática que hace parte de la aritmética que se llama módulo (representado en programación por "%") donde el resultado es el residuo de la división de dos números. Sabemos que un número par que se divide entre 2, el resultado será un número entero. Esto se llama ser divisible. Si el 7 se divide entre 3 da un número decimal. Entonces el 7 y el 3 no son divisibles, o sea que el residuo no es 0. Si yo tomo un número y lo divido entre 1, siempre dará el mismo número. Si el número 2 lo divido entre 1 me da 2. El residuo es 0 porque el número es entero. Si el número 2.1 lo divido entre 1 me da 2.1. El residuo no es 0 ya que es decimal. Esto quiere decir que si un número dividido entre 1 me da decimal, el residual no es 0. No había otra manera sencilla de decirle a lua si un número es decimal porque no existe una función "is_integer" o "es_entero" o algo así. Entonces se me ocurrió poner: math.mod(numero, 1) != 0 para definir si el número es decimal. Regla importante sobre el return: Si el return está en una función, no se terminará la quest ahí. Solo devolvería algo. Si llegáramos a poner sentencias debajo de nuestro bloque if: item_editor_camilo_lib.startItemEditor = function() local _, item_table = mysql_direct_query(string.format("SELECT addon_type FROM player.item_proto WHERE vnum = %s;", item.vnum)) if tonumber(item_table[1].addon_type) == -1 then item_editor_camilo_lib.switchingMeanSkill() else syschat("Este objeto no se puede editar.") end --lo que esté de aquí para abajo se ejecutaría end y también: quest item_editor_camilo begin state start begin when 11000.take or 11002.take or 11004.take begin item_editor_camilo_lib.startItemEditor() --se ejecutaría de aquí para bajo si hubiese algo... end end end Así que pensemos esto: si retornamos en una función, se devolverá un dato. Si retornamos en un bloque when, se detendrá la quest. Ahora vamos a meter la segunda parte de nuestra función: item_editor_camilo_lib.switchingMeanSkill = function() say_title_center("Editor de ítems By Camilo[ENTER]") say_item_vnum(item.vnum) say("Ingresa la media que quieres sacar") local mean_chosen = tonumber(input()) if mean_chosen == nil or mean_chosen < 0 or math.mod(mean_chosen, 1) != 0 then return end say_title_center("Editor de ítems By Camilo[ENTER]") say_item_vnum(item.vnum) say("Ingresa la habilidad que quieres sacar") local skill_chosen = tonumber(input()) if skill_chosen == nil or skill_chosen < 0 or math.mod(skill_chosen, 1) != 0 then return end --Lo nuevo agregado de aquí para abajo local switch_max_amount = 1000000 local mean_calculated, skill_calculated, switchs = get_mean_skill(mean_chosen, skill_chosen, switch_max_amount) if switchs <= switch_max_amount then if mean_calculated == 0 then item.set_value(0, 71, skill_calculated) item_editor_camilo_lib.addAttr(4) elseif skill_calculated == 0 then item.set_value(0, 72, mean_calculated) item_editor_camilo_lib.addAttr(4) else item.set_value(0, 72, mean_calculated) item.set_value(1, 71, skill_calculated) item_editor_camilo_lib.addAttr(3) end else syschat("No dopó. Inténtalo de nuevo") end end La función get_mean_skill es de source y la he creado a partir del source game en item_addon.cpp. Ese archivo define la distribución de la media y la habilidad y devuelve la media y habilidad que haya salido. En los archivos adjuntos estará la función get_mean_skill que debes agregarla al source game en questlua_global.cpp. Toda función que no tenga prefijo o no haga parte de una agrupación debe ir en questlua_global. Si fuera pc.get algo, iría en questlua_pc.cpp. Si fuera item.set algo, iría en questlua_item.cpp. Entiendes el patrón? Nota: debes fijarte cómo se declara la función, si es con ALUA o con int; siempre sigue el patrón... Aparte de agregarla en source game, debes agregar el nombre en quest_functions. Voy a mostrarles un poco la función. No hay que programar nada, solo explico un poco: Parte de questlua_global.cpp ALUA(_get_mean_skill) { if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3)) { return 0; } int mean = (int)lua_tonumber(L, 1); int skill = (int)lua_tonumber(L, 2); int switch_max = (int)lua_tonumber(L, 3); int iSkillBonus = 0; int iNormalHitBonus = 0; int switching = 0; for (int i = 0; i < switch_max; i++) { iSkillBonus = MINMAX(-30, (int)(gauss_random(0, 5) + 0.5f), 30); iNormalHitBonus = 0; if (abs(iSkillBonus) <= 20) iNormalHitBonus = -2 * iSkillBonus + abs(number(-8, 8) + number(-8, 8)) + number(1, 4); else iNormalHitBonus = -2 * iSkillBonus + number(1, 5); switching = i+1; if (iNormalHitBonus >= mean || iSkillBonus >= skill) break; } lua_pushnumber(L, iNormalHitBonus); lua_pushnumber(L, iSkillBonus); lua_pushnumber(L, switching); return 3; } Si no ponemos un límite en cambios, se iría a ciclo infinito y se paraliza el juego si una persona pone una media inexistente, por ejemplo 61 o mayor. switch_max es el máximo de cambios que le pondremos a la función. Es el 3° parámetro de la función. La función recibe tres parámetros, la media mínima que debe salir, la habilidad mínima que debe salir y la cantidad máxima de cambios que debe hacer el ciclo. Lo que está dentro del ciclo es muy parecido a la función de addon_type.cpp. La diferencia es que cuento cada cambio y rompo el ciclo cuando me salga la media o habilidad. El retorno se hace así como lo puse allí, con los lua_push y la cantidad de valores retornados los debo poner en al final, en este caso return 3. Devuelvo la media que salió, la habilidad que salió y la cantidad de veces que se hizo. La cantidad de veces serían los cambios. Recuerden que hay una línea que se debe agregar en ese mismo archivo para que pueda funcionar. Esto está en el archivo adjunto. Muchos me preguntan que cuál es la máxima media y la máxima habilidad. Esta función se probó millones de veces; la media nunca pasó de 60, y la habilidad nunca pasó de 27. En la tercera variable puse switchs, esos son los cambios que se hicieron. Ahora le digo al sistema que si son menores o iguales a la cantidad de cambios máximos, entonces siga. Pero si no, que diga que no dopó. Como máxima cantidad de cambios que hará el source pongo 1000000, es un buen valor. ¿Qué sucede si pongo 10 millones, por ejemplo? El servidor mientras realiza ese proceso le dará prioridad y el juego se quedará quieto. En cambio si pongo 1000000 demorará milisegundos y pasará desapercibido. Variables globales vs locales: Las variables locales mueren cuando se termina un bloque en la jerarquía del when o function. Mueren quiero decir que se vacían, o sea que no se podrán usar luego en otros bloques de esa jerarquía. Las variables globales reservan los datos y cada vez que haces rel q se cargan de nuevo. Si vas a usar una variable global te debes asegurar de que no se interfiera con otra con el mismo nombre. Cuando uses variables locales asegúrate que solo las vayas a usar en ese bloque. No dejes variables con valores almacenados y no los vayas a usar, no llenes el sv de basura. Las variables globales se crean normal, solo no hay que poner el "local". Pondré el max_switch_amount como variable global en el archivo adjunto para que puedan ver. Ahora tengo que validar si la media es 0 o la habilidad es 0 porque en los bonus no se muestra cuando un bonus tiene valor 0. Esta parte es fácil, si alguno de los dos es 0, entonces los bonus que agrego sería 4. Si los dos bonus son diferentes de 0, entonces agrego 3 bonus más. Esto es para que me queden los 5 bonus. La parte del item.set_value. Esta función es de source game. Aparece en questlua_item.cpp. Funciona así: item.set_value(num_bonus, id_bonus, valor_bonus) Al ítem seleccionado (sabemos que el NPC lo seleccionó y cada vez que hagas item. algo, hará algo con ese ítem) se le asignará un bonus. num_bonus me refiero a los números entre 0 y 4 de los bonus. Siendo 0 el primer bonus, me refiero al orden. id_bonus es el bonus número de bonus que aparece declarado en source game en constants.cpp. Si vas allí, busca: const TApplyInfo aApplyInfo[MAX_APPLY_NUM] = En esa tabla aparecerán los nombres de los bonus, antecedidos por POINT_, y en comentario aparece el id. Para el 71 es SKILL_DAMAGE_BONUS. Para el 72 es NORMAL_HIT_DAMAGE_BONUS. En el questlib.lua aparecen los bonus pero están incompletos porque hace años no se toca la tabla apply. Por eso en los archivos adjuntos voy a agregar los demás bonus que salen en los objetos. item_editor_camilo_lib.addAttr(4) Esta función sería la que agregaría esos siguientes bonus. Para que no se vea abrumador, no pondré la función completa. La pondré por partes. item_editor_camilo_lib.addAttr = function(bonus_count) available_attr = item_editor_camilo_lib.getAvailableAttr() end Esta nueva función es la de agregar bonus. Ese Attr significa Attribute o atributos. El parámetro que recibe se llama bonus_count y es el que nos va a decir cuántos bonus se le van a agregar al ítem. Para agregar esos bonus primero necesitamos saber cuáles son los bonus que podemos agregar al objeto porque no podemos meter HP en una arma, por ejemplo. Creamos la función getAvailableAttr() item_editor_camilo_lib.getAvailableAttr = function() local id_types_base = item_editor_camilo_lib.getApplyTypeBase() local r, item_attr_table = mysql_direct_query(string.format("SELECT apply as name_apply, prob, lv1, lv2, lv3, lv4, lv5 FROM player.item_attr WHERE %s > 0;", item_editor_camilo_lib.getNameSubtype(item.type, item.sub_type))) local available_attr = {} for i = 1, r do local name_apply = item_attr_table[i].name_apply local id_apply = apply[name_apply] if not item_editor_camilo_lib.isAttrIncluded(id_apply, id_types_base) then table.insert(available_attr, {}) local current_pos = table.getn(available_attr) available_attr[current_pos][0] = id_apply available_attr[current_pos][1] = name_apply available_attr[current_pos][2] = apply_human[id_apply][2] available_attr[current_pos][3] = {} for j = 1, 5 do available_attr[current_pos][3][j] = item_attr_table[i]["lv"..j] end available_attr[current_pos][4] = item_attr_table[i].prob end end return available_attr end Esto se ve complejo pero vamos a desglosarlo. Necesitamos quitar de los bonus disponibles (aquellos que tienen un 5 en la tabla player.item_attr en la db según el tipo de ítem) los que ya tiene de base el ítem. Si el armadura tiene HP de base, no dejaremos que salga HP en bonus, por ejemplo. La idea es guardar esos types de bonus (o sea, el id) en una tabla para luego comparar y quitarlos de la tabla de disponibles. Creamos la función: item_editor_camilo_lib.getApplyTypeBase = function() local _, apply_type_base = mysql_direct_query(string.format("SELECT applytype0, applytype1, applytype2 FROM player.item_proto WHERE vnum = %s;", tonumber(item.vnum))) local id_types_base = {} for i = 0, 2 do if tonumber(apply_type_base[1]["applytype"..i]) > 0 then table.insert(id_types_base, apply_type_base[1]["applytype"..i]) end end return id_types_base end La query lo que hace es recoger los type que están en player.item_proto de ese objeto. No hay nada raro ahí. Les mostraré en el caso de un Brazalete de plata +9 para que el ejemplo sirva. Ya que este brazalete tiene HP de base y en bonus también tiene disponible el HP. La tabla arroja el type 7 que es velocidad de ataque y el 1 que es HP. Recuerda revisar constants.cpp para ver los ids o types de los bonus. Ahora viene la parte de meter una variable en la tabla que llegó de la query. No podía poner esto: apply_type_base[1].applytype..i Aunque pensarás, bueno, podríamos hacer esto: apply_type_base[1].applytype0 apply_type_base[1].applytype1 apply_type_base[1].applytype2 Pero tendríamos que hacer tres if evaluando si el número es mayor a 0 porque sería inútil llenar la tabla de ceros. Finalmente nos queda: apply_type_base[1]["applytype"..i] Lo demás ya lo expliqué más arriba. Bien, para insertar en una tabla debemos poner table.insert(nombre_tabla, valor_para_meter) Cuando insertamos en una tabla, se agrega un nuevo valor en la tabla, o sea, si está vacía, se agregará el valor en el primer índice. En lua los índices empiezan en 1. Si hacemos 3 inserts, la tabla queda así: [1] = valor1, [2] = valor2, [3] = valor3 Retornamos la tabla. La idea de retornar las tablas es que se puedan reutilizar en otras ocasiones. Ya si quisieras crear otra quest donde necesites obtener los bonus base del equipamento, solo llamas a la función y ya. local r, item_attr_table = mysql_direct_query(string.format("SELECT apply as name_apply, lv1, lv2, lv3, lv4, lv5 FROM player.item_attr WHERE %s > 0;", item_editor_camilo_lib.getNameSubtype(item.type, item.sub_type))) Vamos por esta parte: Esta query es la tabla player.item_attr pero lo que nos importa es sacar los bonus de cierto tipo de equipo. Es decir, arma, armadura, ciertos acc, etc. Revisa la tabla player.item_proto, vas a ver que hay columnas que representan el tipo de equipamento, y en esa columna donde esté el 5 es porque admite el respectivo bonus. Ejemplo para armas, aquí quiere decir que en las armas sale CON (vit), INT, STR, DEX, CAST_SPEED (velocidad de hechizo) y así... La prob no la tendremos en cuenta que que este es un editor, no un dopador. Sobre los lvs: En el source game item_attribute.cpp está la lógica de cómo funciona esto, pero no lo voy a explicar ahora porque esto es un editor, solo mostraremos los 5 valores y el que se seleccione, ese queda. En la query le estamos diciendo al sistema que seleccione la columna apply. ¿Qué es ese "as"? "as" significa "como". Seleccione "apply" como "name_apply", es decir, le estoy cambiando el nombre. ¿Por qué hago esto? porque si no lo hago, habrá redundancia con otro apply (una tabla) que está en el questlib que utilizaremos ahorita. Lo explico mejor: Ya sabemos que hay una columna llamada apply, que termina siendo una tabla aquí en lua. También sabemos que hay una tabla llamada apply que está en questlib.lua y que contiene los nombres de los bonus con sus ids. Al llamar a apply, el sistema a cuál de los dos tomará? ... Eso se llama redundancia. Seguro el apply nuevo de la query reemplaza al que ya teníamos, y perderíamos los datos. Ahora sí, en la query le decimos al sistema que seleccione apply con el nuevo nombre de name_apply, además seleccione la columna lv1, lv2... lv5 de la tabla player.item_attr donde [tipo de equipamento] sea mayor a 0. Cuando obtengamos la tabla de esa consulta, la columna no será apply sino name_apply. Al ejecutar la query, poniendo en %s weapon, obtenemos: Ahora se va viendo la forma. En el %s va el tipo de equipamento. Revisa en player.item_attr las columnas. En mi caso tengo weapon (arma), wrist (brazalete), foots (zapatos), neck (collar), head (casco), shield (escudo), ear (pendientes), pendant (talismán) y glove (guantes). Vayamos a esta parte de esa línea: item_editor_camilo_lib.getNameSubtype(item.type, item.sub_type) Aquí debemos crear una función donde obtengamos ese tipo de equipamento. Ya sabemos que lo obtendremos dando el type y el subtype. Si es un arma, el type es 1. Si es cualquier otro equipamento el type es 2. Dentro del type 2 hay varios subtypes que determina si es brazalete, zapatos, casco, etc. item_editor_camilo_lib.getNameSubtype = function(attr_type, attr_subtype) if attr_type == 1 then return "weapon" elseif attr_type == 2 then local name_subtypes = { [0] = "body", [1] = "head", [2] = "shield", [3] = "wrist", [4] = "foots", [5] = "neck", [6] = "ear", [7] = "pendant", [8] = "glove" } return name_subtypes[attr_subtype] end end Esta estructura hasta el día de hoy es la más eficiente para mí. Diferencia entre hacer varios if y hacer if, elseif, else: Si pongo varios if, se ejecutarán todos. Si pongo un if con varios elseif, se ejecutará solo el que cumpla, en orden. Por lo tanto, el programa haría menos instrucciones si lo haces con elseif, para este caso. Ejemplo con solo if: local level = pc.level if level > 15 then chat("Soy mayor que 15") end if level > 30 then chat("Soy mayor que 30") end if level > 50 then chat("Soy mayor que 50") end Va a mostrar: Soy mayor que 15 Soy mayor que 30 Soy mayor que 50 Dado que es la misma variable, conviene usar if, elseif, end para que muestre solo un resultado, así: local level = pc.level if level > 15 then chat("Soy mayor que 15") elseif level > 30 then chat("Soy mayor que 30") elseif level > 50 then chat("Soy mayor que 50") end Si eres nivel 51, de todas maneras te dirá Soy mayor que 15 pero en este caso de niveles es mejor hacerlo desde el más alto hasta el más bajo: local level = pc.level if level > 50 then chat("Soy mayor que 50") elseif level > 30 then chat("Soy mayor que 30") elseif level > 15 then chat("Soy mayor que 15") end Soy mayor que 50 Ahora sí. Aunque en nuestro caso da igual porque retornamos directamente el valor en cada if o elseif, pero se ve mejor programado. Hay veces que es necesario utilizar la estructura [clave] = valor como en este caso con los subtypes. local name_subtypes = { [0] = "body", [1] = "head", [2] = "shield", [3] = "wrist", [4] = "foots", [5] = "neck", [6] = "ear", [7] = "pendant", [8] = "glove" } Esto es mejor que hacer esto: local name_subtypes = { {0, "body"}, {1, "head"}, {2, "shield"}, {3, "wrist"}, {4, "foots"}, {5, "neck"}, {6, "ear"}, {7, "pendant"}, {8, "glove"} } ¿Por qué? porque para obtener el nombre del subtipo es mejor hacerlo en una línea que hacerlo en un ciclo. En la primera parte sería muy fácil, solo haces esto: name_subtypes[attr_subtype] En la segunda parte tendrías que hacer esto: for i = 0, table.getn(name_subtypes) do if attr_subtype == name_subtypes[i] then return attr_subtype end end La razón es porque, si le tienes un id a cada objeto de tu tabla, solo accedes a él normalmente con tabla[id]. En cambio si no tienes un id para tus objetos en la tabla, tienes que recorrerla y comparar hasta encontrar la coincidencia. Dejo aquí el croquis de donde vamos: item_editor_camilo_lib.getAvailableAttr = function() local id_types_base = item_editor_camilo_lib.getApplyTypeBase() local r, item_attr_table = mysql_direct_query(string.format("SELECT apply as name_apply, prob, lv1, lv2, lv3, lv4, lv5 FROM player.item_attr WHERE %s > 0;", item_editor_camilo_lib.getNameSubtype(item.type, item.sub_type))) --Vamos aquí local available_attr = {} for i = 1, r do local name_apply = item_attr_table[i].name_apply local id_apply = apply[name_apply] if not item_editor_camilo_lib.isAttrIncluded(id_apply, id_types_base) then table.insert(available_attr, {}) local current_pos = table.getn(available_attr) available_attr[current_pos][0] = id_apply available_attr[current_pos][1] = name_apply available_attr[current_pos][2] = apply_human[id_apply][2] available_attr[current_pos][3] = {} for j = 1, 5 do available_attr[current_pos][3][j] = item_attr_table[i]["lv"..j] end available_attr[current_pos][4] = item_attr_table[i].prob end end return available_attr end Con la tabla de los tipos de bonus base y los tipos de bonus que pueden salir por cada equipo, sacaremos los tipos de bonus disponibles en nuestro ítem y los meteremos en una tabla. Vamos de 1 hasta r (recuerden que es la cantidad de registros que arroja la tabla de la query) e insertaremos en orden los bonus así: Posición 0: el id, es decir, el número que aparece en constants.cpp Posición 1: el nombre del apply, o sea, el nombre en mayúscula en inglés separado de guiones bajos que está en constants.cpp Posición 2: el nombre que se mostrará a la persona en el juego, yo lo llamo humano, o sea, leíble para los humanos. Posición 3: los valores lv1 hasta 5 Para el nombre es muy fácil: local name_apply = item_attr_table[i].name_apply Yo lo expliqué cuando había un solo registro, que era poniendo [1]. Para cuando son varios registros estamos usando este ciclo for con la variable de control i. Para el id_apply: ahora sí iremos a la tabla apply del questlib.lua. Si la revisas, te fijas que en las [clave] = valor, en clave está el nombre del tipo de bonus que está en constants.cpp Solo accediendo a la posición del bonus obtendremos el id, es muy sencillo. Lo importante de este ciclo es filtrar esos tipos de bonus base, así que debemos identificarlos. if not item_editor_camilo_lib.isAttrIncluded(id_apply, apply_type_base) then Cada iteración del ciclo hará referencia a un bonus. En cada bonus evalúa si el ítem ya lo tiene incluido o no en el item_proto. Literalmente dice: si no está el atributo incluido, entonces En los parámetros pongo el id_apply y id_types_base porque voy a tomar el bonus actual y lo voy a comparar con los bonus de esa tabla; si lo encuentra entonces retornará true. Si no, entonces hay que agregarlo a nuestra tabla de disponibles. Veamos cómo se hace eso: item_editor_camilo_lib.isAttrIncluded = function(id_apply, table_attr_base) for _, id in pairs(table_attr_base) do if tonumber(id) == tonumber(id_apply) then return true end end return false end Creamos la función isAttrIncluded. Uso del pairs: En los ciclos no siempre se recorre desde un valor hasta cierto otro valor contando de 1 en 1 o de 2 en 2. A veces necesitamos recorrer cada elemento existente. Digamos que tenemos una tabla así: local tabla = { [1] = "uno", [2] = "dos", [3] = "tres", ["uno"] = "UNO", [100] = "cien" } Si ponemos un for así: for i = 1, table.getn(tabla) do --pues tiene 5 elementos chat(tabla[i]) end La salida sería: uno dos tres --aquí daría error, no existe la posición 4 --aquí error, no existe la posición 5 Faltaría el UNO y el cien además. Para mostrar solo los números haríamos esto: for k, v in ipairs(tabla) do --key: value --clave: valor chat(k..": "..v) end La salida sería: 1: uno 2: dos 3: tres 100: cien Así es como funciona el ipairs. Ahora falta otro, el pairs. for k, v in pairs(tabla) do --key: value --clave: valor chat(k..": "..v) end La salida sería: 1: uno 2: dos 3: tres uno: UNO 100: cien Ven la diferencia? realmente no sé si el orden sea ese pero lo importante es que pairs muestra tanto posiciones con texto como números, y el ipairs muestra solo posiciones de números. Yo puse pairs en la quest pero da igual porque no hay posiciones en texto. Cuando pongo if alguna_variable then, estoy diciendo que si la variable es nula o tiene algo que no sea false, entrará al if. La única razón para que no entre es que el valor de la variable sea false. if not item_editor_camilo_lib.isAttrIncluded(id_apply, id_types_base) then Si el actual bonus del ciclo no se encontró en la tabla de bonus base del objeto, entonces retona false y entrará al if. La estructura de mi tabla de disponibles será así: [1] = { --ese 1 es la posición que se agrega automáticamente por hacer el table.insert. [0] = id_apply, --ej: el 1 [1] = name_apply, --el nombre del POINT, el que está en constants.cpp, ej: "MAX_HP" [2] = nombre_humano, --quiero decir, el bonus que lo pueda leer un humano y lo entienda, ej: "Máx. HP" [3] = { [1] = value_bonus1, --estos son los levels de player.item_attr [2] = value_bonus2, [3] = value_bonus3, [4] = value_bonus4, [5] = value_bonus5 } } Lo hago poniendo el index en orden porque será útil más adelante. Se preguntarán que por qué no pongo simplemente: available_attr[i][0] = id_apply, por ejemplo. Pues intentaré explicarlo como pueda. Como hay un if que me restringe algunos bonus, si yo pongo un if, mi tabla ya no estará en orden. --vamos con la i = 1 --este bonus no lo tiene, ok, agregado [1] = {--[[todo lo del bonus aquí--]]}, -- i = 2 --este bonus no lo tiene, ok, agregado [2] = {--[[todo lo del bonus aquí--]]}, -- i = 3 --este bonus sí está en la tabla, no entra al if, no se agrega -- i = 4 --este bonus no lo tiene, ok, agregado [4] = {--[[todo lo del bonus aquí--]]}, --El orden quedó 1, 2, 4... y no nos servirá para nuestro ciclo de más adelante En vez de la i puse que se insertara la nueva tabla en la última posición y fue sencillo: table.insert(available_attr, {}) local current_pos = table.getn(available_attr) available_attr[current_pos][0] = id_apply Obtengo el tamaño de mi tabla, y obviamente ese tamaño será la última posición -1. Ahora la parte del apply_human. ¿Qué significa eso? available_attr[current_pos][2] = apply_human[id_apply][2] Es una tabla global (creada en el questlib.lua) hecha por mí y adjunta abajo al final, en donde defino nuevamente los bonus del apply pero en una estructura diferente y con los nombres que aparecen en el locale o muy parecidos. Es decir, en vez de que salga "MAX_HP", que salga "Máx. HP". Otro ejemplo, en vez de que salga "CRITICAL_PCT", salga "Probabilidad de golpes críticos +x%". El % también lo incluyo. Estos textos no aparecen en los bonus del ítem obviamente, recuerden que esto es quest, solo aparecerá en la pantalla negra en los botones. Se puede crear una tabla dentro de otra. En cada bonus creé esa estructura que puse arriba. También hice una nueva para los levels, ya que son 5, y son en orden, la mejor manera era crear una tabla. Ya tenemos los bonus disponibles. Ahora sí volvamos a addAttr item_editor_camilo_lib.addAttr = function(bonus_count) available_attr = item_editor_camilo_lib.getAvailableAttr() item_editor_camilo_lib.makeSelectUI() --vamos a crear ahora el select end He creado la variable available_attr como global ya que la usaré en otra función sin tener que enviarla por parámetro. Crearemos el select_table. La diferencia entre un select y un select_table es que en el select envías los textos separados por coma, mientras que en el select_table envías una tabla. Las tablas son dinámicas. --Si quisieras poner los números del 1 al 5 en texto en un select, lo harías así: local sel = select("Uno", "Dos", "Tres", "Cuatro", "Cinco") --Podría cambiar los números sin problema al ponerlo en variables local num1, num2, num3, num4, num5 = "uno", "dos", "tres", "cuatro", "cinco" local sel = select(uno, dos, tres, cuatro, cinco) --Pero si quisiera variar la cantidad de textos que hay en el select, ahí ya no se puede. Si solo necesito 3 textos, qué hago con los otros dos que me sobran? --A ese problema nos enfrentamos, pues cada equipamento tiene distintas cantidades de tipos de bonus que pueden salir. Unos tienen 14, otros 15, otros 16, etc. --La solución es crear una tabla y usar select_table. local tabla = {"uno", "dos", "tres", "cuatro", "cinco"} local sel = select_table(tabla) --Ahora quiero dejar solo tres números. local tabla = {"uno", "dos", "tres"} --ya sea que lo haya cambiado con un insert o asignaciones, lo que sea. local sel = select_table(tabla) Ahora sí vamos con el makeSelectUI item_editor_camilo_lib.makeSelectUI = function() type_select_ui, value_select_ui = {}, {} for i = 1, table.getn(available_attr) do type_select_ui[i] = available_attr[i][2] for j = 1, table.getn(available_attr[i][3]) do table.insert(value_select_ui, {}) value_select_ui[i][j] = available_attr[i][3][j] end table.insert(value_select_ui[i], "Volver") end table.insert(type_select_ui, "Cerrar") end Creamos dos tablas, una para los select_table de los nombres en humano de los bonus y otra para los valores en el select_table. Aquí no hay mucha ciencia, solo tomamos el available_attr que es global, y agarramos la pos 2 que es el nombre humano y la pos 3 que son los valores de ese bonus. Hay que darle un valor añadido y es, una vez llenado los valores de la página del bonus, agregarle el botón de "Volver". También una vez agregados todos los bonus, agregar el botón de "Cerrar". Vamos a la última parte: item_editor_camilo_lib.addAttr = function(bonus_count) available_attr = item_editor_camilo_lib.getAvailableAttr() item_editor_camilo_lib.makeSelectUI() --Vamos de aquí para abajo. La función anterior no retorna porque las tablas las dejé globales. local type_saved, value_saved = {}, {} local type_support = {} for i = 1, table.getn(available_attr) do type_support[i] = available_attr[i][0] end table.insert(type_support, "") for i = 6-bonus_count, 5 do say_title(string.format("Elige el %s° bonus", i)) local sel_type = select_table(type_select_ui) if sel_type != table.getn(type_select_ui) then say_title("Elige el valor") local sel_value = select_table(value_select_ui[sel_type]) if sel_value != 6 then table.insert(type_saved, type_support[sel_type]) table.insert(value_saved, value_select_ui[sel_type][sel_value]) table.remove(type_select_ui, sel_type) table.remove(value_select_ui, sel_type) table.remove(type_support, sel_type) else i = i - 1 end else return end end local count = 1 for i = math.abs(bonus_count-5), 4 do item.set_value(i, type_saved[count], value_saved[count]) count = count + 1 end syschat("Hecho!") end Esas tablas type_saved y value_saved son los bonus y los valores del bonus que selecciona la persona. Saved significa guardados o salvados. local type_support = {} for i = 1, table.getn(available_attr) do type_support[i] = available_attr[i][0] end table.insert(type_support, "") Creé una tabla de soporte simplemente por cuestiones de conversión. Necesitaba obtener el id del bonus pero el type_select me da el nombre humano, y el select_table me da la pos de ese nombre humano pero no el id del bonus. Así que hice una tabla del mismo tamaño del select porque también necesitaba eliminarla para cuando la persona seleccionara un bonus. Me explico, cada vez que la persona elije un bonus hay que quitarlo para que no aparezca en las demás páginas porque no se debe repetir. Es por eso que más abajo aparece table.remove. La tabla de soporte tiene esta estructura: type_support = { [1] = 1, --id del bonus [2] = 2, ---... --no siempre será en orden, ya que hay bonus que se saltan } Puse: table.insert(type_saved, type_support[sel_type]) y no: table.insert(type_saved, available_attr[sel_type][0]) porque available_attr como es global, no puedo eliminarle bonus porque puede que interfiera con otro personaje. Así que creo la de soporte para que se le pueda eliminar los bonus. En verdad puse variables globales en varias partes cuando no debería porque era para explicar lo de global y local. Lo explicaré mejor. Si es una variable que cambiará por cada personaje, entonces ponla local. Si la variable se usará por otros personajes , por ejemplo en un evento, entonces ponla global. Explico de nuevo. En este caso el available_attr que lo había puesto global, imagina que dos personas estén usando al tiempo el editor. Puede llegar un momento en que al crear la tabla support de alguno de los dos, tome el available_attr de la otra persona y sean diferentes. En el archivo adjunto no habrá variables globales a excepción de switch_max_amount ya que ese valor no cambia. La llamaría una constante. En esta parte: for i = 6-bonus_count, 5 do es muy sencillo, tienes que tener en cuenta que como esto es la interfaz, esto será equivalente a las páginas de bonus que le mostrarás al personaje. Si hubieras puesto: bonus_count = 5 -> 6-5 = 1 hasta 5. Página 1 hasta la 5 bonus_count = 4 -> 6-4 = 2 hasta 5. Página 2 hasta la 5. Esto en caso de que hubiera salido algún valor 0 de media o habilidad. bonus_count = 3 -> 6-3 = 3 hasta 5. Página 3 hasta la 5. Esto en caso de que salga valores de media y habilidad diferentes de 0. Para cuando le dan al último botón siempre uso mi viejo truco de poner: if sel != table.getn(tabla) then return end En este caso en el sel_type pongo que si le da a "Cerrar", cancele toda la operación. En el caso del sel_value pongo diferente de 6 porque siempre los levels van a ser 5, y el botón 6 es "Volver", así que le resto 1 a mi variable de control para que me devuelva a la página anterior. Ya en la última parte es necesario hacer un truco para sincronizar la cantidad de bonus con las posiciones donde se van a poner. for i = math.abs(bonus_count-5), 4 do Si bonus_count = 5 -> de 0 hasta el 4. Ahí están los 5 bonus. Si bonus_count = 4 -> de 1 hasta el 4. Como ya tiene un valor de media o habilidad diferente de 0, se agregarán los bonus del segundo al quinto. Si bonus_count = 3 -> de 2 hasta el 4. La media y habilidad es diferente de 0, ya tiene dos bonus, se agregan los otros 3. Ese math.abs es una función que ya viene con lua y en todos los lenguajes de programación. En matemáticas el absoluto es volver positivo todo valor. Si no hubiera puesto el absoluto, miren lo que hubiera ocurrido: Si bonus_count = 5 -> de 0 hasta el 4. Aquí está bien porque da 0 Si bonus_count = 4 -> de -1 hasta el 4. Del -1 al 4 hay 6, serían 6 bonus. Si bonus_count = 3 -> de -2 hasta el 4. Del -2 al 4 hay 7, serían 7 bonus. En esta línea: item.set_value(i, type_saved[count], value_saved[count]) Podemos ver que la i debe empezar en 0, y las tablas type_saved y value_saved en 1. Ya saben por qué. Con ese local count = 1 lo hago. Y lo aumento en 1 en cada iteración y ya está. La estructura de type_saved es: type_saved = {1, 18, 17, 16, 22} La estructura de value_saved es: value_saved = {2000, 10, 10, 10, 20} Donde el 1 corresponde con el 2000, el 18 con el 10 y así. Al final pongo "Hecho!" para comprobar que se terminó el editor. Los resultados son los mismos que en el post original del editor. (dejo todo en el adjunto) Muchos éxitos para todos! Post original de la quest: item_editor_camilo.rar
  2. Buena noche gente en mi tiempo libre he estado maquetando esta pagina web, me ha quedado gustando un poco así que he decido compartir lo que he avazando espero se ha de su agrado. Cabe recalcar que es es solo estructura sin funcionamiento alguno, la pagina esta desarrollada con php, para poder ejucutarla necesitan xampp para hacerlo local o de lo contrario un hosting. [Hidden Content]
  3. Hola buenas, Aqui le dejo una guia para los que no saben arreglar los errores de Libs. Cuando tengan todo instalado y les pide ejemplo , libtiff.so.5 ya que mucha gente no sabe arreglarlo aqui le dejo unos comandos.. 1- ln -s /usr/lib32/libIL.so.2 /usr/lib32/libIL.so.1 2- ln -s /usr/lib32/libjpeg.so.11 /usr/lib32/libjpeg.so.8 3- ln -s /usr/lib32/libpng15.so.15 /usr/lib32/libpng16.so.16 4- ln -s /usr/lib32/libstdc++.so.4 /usr/lib32/libstdc++.so.5 5- ln -s /usr/lib32/libtiff.so.4 /usr/lib32/libtiff.so.5 Nose si tendran las libs igual dejo el link para que puedan bajarlos y tendran que subirlos a la ruta cd /usr/lib32 Libs.rar
  4. Buenas a Todos/as En este sección se estará actualizando con proyectos nuevos y sistemas en venta. ***Fast Equip [C++/Python]*** 10/11/2021 ***SwitchBot [C++/Python]*** 27/03/2021 ***Elemental Spell [C++/Python]*** 26/08/2020 ***Stone (Add - Change - Transfer) [C++/Python]*** 05/06/2020 ***Bonus 6&7 [C++/Python]*** 30/04/2020 ***Multi Refine [C++/Python]*** 16/01/2020 ***Cube Renewal [C++/Python]*** 27/09/2019 ***Search Drop Item[C++/Python]*** 25/08/2019 ***Change Bonus Alchemy [C++/Python]*** 20/04/2019 ***Hide Costume Final Version [C++/Python]*** 25/10/2018 ***Emotions V0.1 [C++/Python]*** 04/10/2018 ***MailBox v0.1 [C++/Lua/Python]*** 21/05/2018 ***Use System v0.1 [C++/Lua/Python]*** 07/12/2017 ***Gaya System v0.1 [C++/Lua/Python]*** 06/07/2017 ***GiftsCode V0.1 [C++/Python]*** 11/04/2017 ***Change Bonus V0.1 [C++/Python]*** 16/02/2017 ***Search Chest [C++/Python]*** 31/01/2017 ***Search Shop [C++/Python]*** 23/11/2016 ***Tienda Decoracion [C++/Python]*** 28/10/2016 ***Interfaz Illumina [Python]*** 01/03/2016 PD: *La instalacion de cualquier sistema mencionado aqui es totalmente gratis. *El arreglo de cualquier bug o problema que tenga dicho sistema .Se fixea sin ningun costo. *Se realizan trabajos a pedidos ya sea en c++/python/lua. Precio por skype: felipe.ardila8
  5. Saludos a todos, en mi pais son las 4:23 A.M y me he dado el tiempo de compartir a ustedes y en especial a aquellos que comienzan con un metin, la encriptacion de Arrival2 y seguridad de arrival2. En que consiste: -Poder utilizar la encriptacion colocando la extensión que quieras a los archivos .eix y .epk (Tendrás que buscar una mejor manera ya que ahora todos tendran el descompilador) Debes arrastrar los .eix y epk al Encriptador para obtener el archivo con extension .metin2 y luego debes renombrar la extension por la que quieras usar, en este caso elegi .tester -La seguridad en que no te puedan meter archivos que no corresponden al juego, en caso de que metan alguno les saldrá un cuadro de error como en la imagen a continuacion. Les dejo los archivos aca, espero que les sirva, no puedo darles soporte porque yo no lo cree, solo se que pude meter esto a mi cliente sin problemas, tampoco soy un experto, pero algo es algo, suerte! Descarga: [Hidden Content] VirusTotal: [Hidden Content]
  6. Hello guys, My website for new Updates Skype : masrawy11500 Discord:Dane#7638 Click Here to go on my website! Info: (Skype : masrawy11500) Armors: (darkness(gold,black,silver) ,diamond ,golden ,hugesteal ,bronze ,wolves-brand,Dark-harvest,Lion-revolution,wolves night,Violets,Eden-Sprit , buch_wave ,little_devil ,red_evil ,royal_brain ,shivan_tit ,zhonya) Note : Any group contains (4 Female and 4 male + wolfman + icons + specular data(Gloss) ) If you liked any design of groups, and you need to edit it for you I can edit it(Example => add logo , or delete anything) Red_devil[New]: little_devil[New]: Zhonya[New]: Shivan_tit[New]: Buch_wave[New]: Royal_brain[New]: Dark harvest: Lion revolution: wolves night: Violets: Eden Sprit: darkness(gold): darkness(black): darkness(silver): diamond: golden: hugesteal: pronze: wolves: Costumes: (Sparta,Angels,Vampire,Steampunk,Sns,pirate,devil) Note : Any group contains (4 Female and 4 male + wolfman + Costume Hair) If you liked any design of groups, and you need to edit it for you I can edit it Sparta: Angels: Vampire: SteamPunk: Sns: Pirate: Devils: ----------------------------------------------- Note: Any group have a costume hair ----------------------------------------------------------------------- Info: (Skype : masrawy11500) ------------------------------------------------------------------------- Note : Any group contains (4 Female and 4 male + wolfman + Costume Hair+icons) If you liked any design of groups, and you need to edit it for you I can edit it Dane
  7. Buenas quiero hacer un gran aporte para los que quieran empezar con el mundo de metin2 y para los que ya empezaron. veo que mucha gente busca vdi con freebsd 12.1, o vdi con freebsd 9.2,, ademas de estar listos, también se los deje con instalación FARM ( servidor web), con web instalada todo listo para descargar y montar, aparte le deje 2 clientes uno el que trabaja con del vdi freebsd 12.1 esta en español vieja escuela, y el que trabaja con el vdi freebsd 9.2 es el bestproduction V4 traducido al 30% al español, ambos clientes vienen con patchupdate listo para usar con instalador, cliente español sin patchupdate: [Hidden Content] cliente español con patchUpdate: [Hidden Content] vdi freebsd 12.1+ file + pagina web lista para usar: [Hidden Content] cliente bestprodution v4 + patchUpdate: [Hidden Content] vdi freebsd 9.2 + file +pagina web lista para usar: [Hidden Content] Aquí están los tar.gz de los bestproduction que estaban pidiendo: mysql_max_9_2 y game_max_9_2: [Hidden Content] Para Descomprimir: tar -xzvf mysql_max_9_2.tar.gz y despues colocan otra vez tar -xzvf game_max_9_2.tar.gz Creador de archivos para patchUpdate [Hidden Content] carpeta makepack esta carpeta es el contenido del cliente para poder editar el root y todo el contenido traducirlo u otros. Aqui les dejo el enlace de donde bajar el makepack, como compilarlo y corregir el error de los edificios que no se ven cambiar la carpeta root por la que esta en este enlace [Hidden Content] Datos de acceso: vdi freebsd 12.1=>usuario: root clave: 0424nilmary mysql freebsd 12.1=>usuario: remoto clave: R1.mo_To vdi freebsd 9.2=>usuario: root clave: dev mysql freebsd 9.2=>usuario: root clave: /*bestproduction*/ clientes: usuario: nilsonmax clave:12345678 aunque con la pagina web pueden entrar en registrar y se crean las cuentas que deseen como les dije ya esta listo para montar. para configurar escribirme, usen noip para enlazar las ip dinamica de sus pc, y abrir los puertos y desactivar el firewall sea por antivirus y/o windows aquí les subo unas imagenes , la web, el instalador del metin, el pachtUpate, el metin y unas ventanas que tiene casi listo con todo. Este Server online Metin2Pluton traducido al 80%, ya este mes lo termino al 100%, vendo traducción interesados whatssap +573008935212 ver imágenes de la traducción al 80% [Hidden Content] Aprovecho de poner un enlace acá que me preguntan a cada rato que VPS les puedo recomendar, aquí les dejo el link donde les recomiendo un VPS de paga, digo que trae y porque lo prefiero y no solo para metines sino para los demas juegos online ya que trae buenas prestaciones y a muy buen precio, [Hidden Content] Los enlaces viejos se cayeron estas son imágenes ya mas reciente del proyecto, de las traducciones que estoy vendiendo es el mismo metin pero ya traducido al 80% no al 30% es para que vean lo que trae el metin.
  8. Hello Images Video Download link: Credit :khafkef
  9. Hello guys welcome back again l i'm gonna share you a new style of wedding costume metin2 i hope you like them ,if you like them just do Like and comment if you found any problem or mistakes just comment or pm me Regards, Dane Screens: Download:
  10. Buen dia. Ya que el servidor Metin2 Kai cerro, les queria compartir la pagina de este. esta pagina es un desarrollo propio, esta realizada con un framework que se llama django. Dicho framework usa python como lenguaje de programacion. la pagina web en si es muy segura, tiene sistema de templates nativos, y panel de administracion. cabe destacar que estare actualizando el proyecto con nuevas versiones de django y tambien se van a publicar mas template para dicha pagina. si alguien quiere unirse al desarrollo puede enviar sus comits al proyecto. porque la libero gratis? Esta pagina es un proyecto open source que busca que la gente que no sepa hacer web, cuenten con una web segura y los dejen de timar con web's que todas usan el mismo codigo de base php con funciones que ya estan deprecated ejemplo la funcion mysql. al estar dichas funciones deprecated dichas paginas web's son muy suceptibles a que sean hackeadas, con inyeccion sql. Bueno no siendo mas dejo el enlace a github de la pagina web. [Hidden Content] Proximamente estare publicando como montar la pagina. si necesitan hosting django para esta web pueden contratar conmigo los servicios de hosting. -> www.vps-hosting.es
  11. ◄ ServerFiles ► Premiumlike ◄ ServerFiles ► Kuratox3 ◄ ServerFiles ► Battle2 ◄ ServerFiles ► BestProduction 1.8 ◄ ServerFiles ► Melosia2 ◄ ServerFiles ► Rubinum2 ◄ ServerFiles ► Taviona2 ◄ ServerFiles ► Maro2 / Oldline / Nalunia ◄ ServerFiles ► Mukari2 - Multilenguaje ◄ ServerFiles ► Drakonia2 ◄ ServerFiles ► Elitarius2 ◄ ServerFiles ► DINOS ◄ ServerFiles ► 40K pagas - Gratis ◄ ServerFiles ► con WON listas para montar ◄ ServerFiles ► Rivality2 ◄ ServerFiles ► con 6ta Raza ◄ ServerFiles ► Oficial TR by Frozen ◄ ServerFiles ► Abizu2 ◄ ServerFiles ► Sanii V1.2 [REUPLOAD] ◄ ServerFiles ► Nirvana V2 ◄ ServerFiles ► Athraworld ◄ ServerFiles ► Play PVP Farm ◄ ServerFiles ► Metin2Family ◄ ServerFiles ► Terios.pl ◄ ServerFiles ► Metin2Gravity ◄ ServerFiles ► Titania2 ◄ ServerFiles ► Limpios by Undyne ◄ ServerFiles ► Heroes última versión ◄ ServerFiles ► Avalter.pl ◄ ServerFiles ► Lucian (ingles) ◄ ServerFiles ► Ocelot2606
  12. Amigos estos son archivos de pago que les publico a ustedes valor 300 euros estrabajo de mierda del alex como ustedes loconocen me an contado como es esta mierda. [Hidden Content] SER AGREGO AL TEMA SOURCE Y TODOS LOS ARCHIVOS SALUDOS [Hidden Content] CON MUCHO CARIÑO PARA TODOS ( CONSEJO PARA LA VIDA NO NOS PEGUEMOS DE CHUCHERIAS) [Hidden Content] [Hidden Content] [Hidden Content] SERVI FILES METIN2 PANDORA CON SOURCE AVISAR QUINES VENDAN ESTE ARCHIVO PARA DAÑARLES EL FACEBOOK O HACERLO FAMOSOS [Hidden Content] SUVIRE LO FALTANTE APENAS TENGA BUEN INTERNET SALUDOS
  13. ¡Saludos a todos! Les presento estos set que tengo en venta, los tengo a 10 dolares cada set y 15 dolares dos set, espero que les guste si necesitan ver fotos de las armas en juego me pueden hablar al whatsapp: +56922228698 (nuevo numero) cada set viene con sus archivos listo para implementar
  14. pensaba en lo que debería suceder para que metin2 tenga un futuro decente entre los MMORPG y talvez sea algo imposible, creo que se debería unir todo los mapas en solo uno, es decir que no halla portales y puedas ir a cualquier sitio por tierra, aire o mar. agregar montura que pueda volar o planear muy alto. agregar jefes especiales que solo podrán llegar en barco hablando con el pescador, este entregara un ticket con el que todos podrán abordar a cierta hora especifica. esas fueron mis 3 sugerencias
  15. Hi, this is the Alloy Metin2 team. We would like to present you a new kingdom of shinsoo, which will appear on our server. Let me know how you like it;) Our project is under development, all interested are welcome to visit our official facebook. [Hidden Content] Best Regards, Team Alloy.
  16. Aqui esta el video para poder hacerlo. El video es completamente ablando, pero creo que no se escucha bien. [Hidden Content] links: Para Descargar: [Hidden Content] las plantillas web : [Hidden Content]foro/plantillas-phpbb3/ Si te sirvió da gracias no cuesta nada
  17. Saludos a todos, les dejo acá este programa muy útil a la hora de pasar poder pasar un gr2 a mesh para meterlo al 3ds max. Me costo demasiado encontrarlo, no se para los demás, pero por lo menos en Metin2Zone no lo encontré, si esta, pues avísenme jajaj Aca les dejo el link, el cual incluye una carpeta donde trae unos mesh de las armas oficiales para comparar dimensiones de las armas: Link: [Hidden Content] Para los que no saben usarlo, es muy simple, buscan algun GR2 en el directorio '' D:\ymir wrok\item\weapon '' y lo colocan en la carpeta donde esta el decodificador. Arrastra el GR2 al ''grnreader.exe" y les saldrán las siguientes ventanas: Le dan a ''SI'' a todo
  18. Bueno aquí les traigo un pack de todos los ítem que se pueden adaptar a un cliente de metin2. Los iconos que tenemos son de Inferna sacado por mi esta todo completo lo único que falta son las mascotas y las monturas ya que tendrían que implementar unas mascotas y monturas igual que las de inferna espero que les vengan bien un saludo. [Hidden Content] [Hidden Content] [Hidden Content] [Hidden Content] [Hidden Content] Link de descarga de todo el pack completo aquí abajo. Descargar pack completo.
  19. Hola comunidad de metin2 me dedico a traducir metin2, los interesados que le traduzca su metin 2 comunicarse conmigo al Whassap 3008935212. Aquí les diré unos trucos de como traduzco las quest el itemproto, mobproto y otros codigos mas rápido y correctamente. En el primer video les muestro como traduzco una quest con un traductor llamado Deepl es una forma de traducirlo mas cómodo y te dice automáticamente como quieres que las palabras se traduzcan y la recuerda para la próxima traducción. y el segundo video como traduzco y como cambio los espacio en blanco por tabulador con una búsqueda atreves de expresión regular que busca solo lo que quiero buscar en un sitio especifico que es el primer espacio en blanco en cada fila. En el tercer video uso un plugin para notepad++ se llama compare ver los trucos completos visitar mi blog: [Hidden Content]
  20. Hey guys welcome back again today i'm gonna share you a new free armor, costume metin2 we take about (assassin auora Kingdom) that's i callded it xd Screens ingame Video -Note- files Contain *name_01.gr2 with attach *name_02.gr2 without attach *msm files *attach as mse file *Icons *Hairs Link Download [Hidden Content] VirusTotal [Hidden Content] If you have any questions about the models feel free to ask me, I will help you always. Have a Great Day and Good Luck!! Regards, Dane
  21. Hello Download link:
  22. Buenas Alguno sabe de como implementar el Baúl de Aprendiz I cuando creo Nueva cuenta no sale.
  23. Hello ,i share you today a free 3d wing effect from : ridack3d.jimdo.com -Screen : -Video : [Hidden Content] -You can found more on : [Hidden Content] -My Skype : hamdou5551 -For Download it go on : [Hidden Content]
×
×
  • Create New...