-- OT-MMO: ventana de crafteo de las estaciones de Refugio. Solo muestra y pide: el servidor -- (server/scripts/crafteo/crafteo.lua) valida, quita materiales y crea el objeto. Opcode extendido 103: -- «R|profesion|nombre|nivel|xp|xpNivel|xpSiguiente|r1~r2~...» -- receta: id^nombre^nivel^resultado^cantidad^ingredientes^xp^equipo^nivelPj^calidadMin^indice^tramo (los cuatro últimos y el -- nombre de cada ingrediente «id:necesita:tiene:nombre», desde el 27-oct; con un servidor viejo faltan y no pasa nada) -- El cliente envía «F|receta|veces». local OPCODE = 103 -- Nombres en español de los materiales (los de Tibia vienen en inglés). local NOMBRES = { [5880] = 'Mineral de hierro', [5901] = 'Madera', [3736] = 'Hierba estrella', [3115] = 'Hueso', [3110] = 'Lingote de hierro', [5941] = 'Mango', [5878] = 'Cuero', [3577] = 'Carne' } crafteoController = Controller:new() local ventana = nil local recetas = {} local nivelOficio = 1 local elegida = nil local function nivelPj() local p = g_game.getLocalPlayer() return p and p:getLevel() or 999 -- sin personaje (capturas de prueba) no se bloquea: el servidor valida igual end -- Rótulo del tramo de una receta (la lista va agrupada). local function tramo(r) if r.tramo ~= '' then return r.tramo elseif r.indice >= 110 then return 'Índice 110 · personaje 100' elseif r.pj >= 90 then return 'Umbral de Brasa · personaje 90' end return 'Oficio 1-20' end local function nombreDe(id, nombre) if nombre and nombre ~= '' then return nombre end if NOMBRES[id] then return NOMBRES[id] end local t = g_things.getThingType(id, ThingCategoryItem) return t and t:getName() ~= '' and t:getName() or 'objeto' end local function puede(r) if nivelOficio < r.nivel or nivelPj() < r.pj then return false end for _, i in ipairs(r.ing) do if i.tiene < i.necesita then return false end end return true end local function mostrar(r) if not ventana or not r then return end elegida = r local d = ventana.detalle d.marco.resultado:setItemId(r.resultado) d.marco.resultado:setItemCount(r.cantidad) d.nombre:setText(r.nombre) local bloqueada = nivelOficio < r.nivel or nivelPj() < r.pj local req = string.format('Oficio %d', r.nivel) if r.pj > 0 then req = req .. string.format(' · personaje %d', r.pj) end d.requisito:setText(req .. string.format(' · +%d de experiencia', r.xp)) d.requisito:setColor(bloqueada and OtmmoTheme.color.errorText or OtmmoTheme.color.text2) local calidad = '' if r.equipo and r.calidadMin >= 2 then calidad = 'Sale poco común, rara o épica. Mejora con tu oficio.' elseif r.equipo then calidad = 'Sale con calidad: común, poco común, rara o épica. Mejora con tu oficio.' end if r.indice > 0 then calidad = calidad .. string.format(' Índice de objeto %d.', r.indice) end d.calidad:setText(calidad) d.ingredientes:destroyChildren() for _, i in ipairs(r.ing) do local fila = g_ui.createWidget('FilaIngrediente', d.ingredientes) fila.icono:setItemId(i.id) fila.nombre:setText(nombreDe(i.id, i.nombre)) fila.cuenta:setText(string.format('%d / %d', i.tiene, i.necesita)) fila.cuenta:setColor(i.tiene >= i.necesita and OtmmoTheme.color.ok or OtmmoTheme.color.errorText) end local ok = puede(r) ventana.botones.uno:setEnabled(ok) ventana.botones.cinco:setEnabled(ok) end local function abrir(partes) nivelOficio = tonumber(partes[4]) or 1 local xp, xpNivel, xpSig = tonumber(partes[5]) or 0, tonumber(partes[6]) or 0, tonumber(partes[7]) or 1 recetas = {} for _, fila in ipairs((partes[8] or ''):split('~')) do local f = fila:split('^') if #f >= 8 then local r = { id = tonumber(f[1]), nombre = f[2], nivel = tonumber(f[3]), resultado = tonumber(f[4]), cantidad = tonumber(f[5]), xp = tonumber(f[7]), equipo = f[8] == '1', pj = tonumber(f[9]) or 0, calidadMin = tonumber(f[10]) or 1, indice = tonumber(f[11]) or 0, tramo = f[12] or '', ing = {} } for _, par in ipairs(f[6]:split(',')) do local id, necesita, tiene, nombre = par:match('^(%d+):(%d+):(%d+):?(.*)$') if id then r.ing[#r.ing + 1] = { id = tonumber(id), necesita = tonumber(necesita), tiene = tonumber(tiene), nombre = nombre } end end recetas[#recetas + 1] = r end end if not ventana then g_ui.importStyle('/game_viaje/valdren') -- marco y estilos VALDREN (modules/game_viaje/valdren.otui) ventana = g_ui.displayUI('crafteo') OtmmoVentanas.recordar(ventana, 'crafteo') -- posición entre sesiones (fase 4) end ventana:setText(partes[3] or 'Crafteo') ventana:show() ventana:raise() ventana:focus() ventana.oficio:setText(string.format('%s · nivel %d', partes[3] or '', nivelOficio)) local frac = xpSig > xpNivel and math.max(0, math.min(1, (xp - xpNivel) / (xpSig - xpNivel))) or 1 ventana.barra.fill:setWidth(math.max(1, math.floor((ventana.barra:getWidth() - 2) * frac))) ventana.xpTexto:setText(string.format('%d / %d', xp, xpSig)) local lista = ventana.lista lista:destroyChildren() local foco, primeraFila, rotulo = nil, nil, nil for _, r in ipairs(recetas) do if tramo(r) ~= rotulo then rotulo = tramo(r) g_ui.createWidget('FilaTramo', lista):setText(rotulo) end local fila = g_ui.createWidget('FilaReceta', lista) primeraFila = primeraFila or fila fila.icono:setItemId(r.resultado) fila.nombre:setText(r.nombre) fila.nivel:setText('Nivel ' .. r.nivel) if nivelOficio < r.nivel or nivelPj() < r.pj then fila.nivel:setText(nivelOficio < r.nivel and ('Requiere oficio ' .. r.nivel) or ('Requiere personaje ' .. r.pj)) fila.nombre:setColor(OtmmoTheme.color.textDisabled) fila.icono:setOpacity(0.4) elseif not puede(r) then fila.nivel:setText('Nivel ' .. r.nivel .. ' · faltan materiales') end fila.onFocusChange = function(w, focused) if focused then mostrar(r) end end if elegida and elegida.id == r.id then foco = fila end end local primero = foco or primeraFila if primero then lista:focusChild(primero, KeyboardFocusReason) end end local function onOpcode(protocol, opcode, buffer) local partes = buffer:split('|') if partes[1] == 'R' then abrir(partes) end end -- Capturas sin servidor (cliente web de prueba): modules.game_crafteo.probar('R|...'). function probar(buffer) onOpcode(nil, OPCODE, buffer) end function fabricar(veces) local protocol = g_game.getProtocolGame() if protocol and elegida then protocol:sendExtendedOpcode(OPCODE, string.format('F|%d|%d', elegida.id, veces)) end end function cerrar() if ventana then ventana:hide() end end function crafteoController:onInit() ProtocolGame.registerExtendedOpcode(OPCODE, onOpcode) end function crafteoController:onTerminate() ProtocolGame.unregisterExtendedOpcode(OPCODE) if ventana then ventana:destroy() ventana = nil end end function crafteoController:onGameEnd() cerrar() end