-- OT-MMO: correo de la Liga del Puerto (servidor: server/scripts/correo/correo.lua, opcode 150). Solo dibuja y pide: -- el servidor comprueba el destinatario, los objetos, el oro y el franqueo, y decide cuándo llega cada carta. -- servidor -> «B|cab|cartas|enCamino» bandeja · «L|carta|adjuntos» carta abierta («L|0» = ninguna) · «D|...» destinatario -- «M|texto» aviso · «S|texto» enviada · «N|n^de^asunto» carta nueva · «I|n» sin leer · «X» cerrar -- cliente -> «A» · «L|id» · «R|id|todo|i|oro» · «V|id» · «B|id» · «C|nombre» · «E|para^asunto^cuerpo^oro|cid:slot:id:n~...» -- Los adjuntos se arrastran desde la mochila a las casillas del borrador: se guarda dónde está el objeto (contenedor y -- casilla) y el servidor lo vuelve a buscar al enviar. Arte: images/ (PixelLab, ver CREDITOS.md). local OPCODE = 150 local ORO, CLARO, TEXTO, TEXTO2, OK, ERROR = '#C7A86B', '#E8D3A2', '#E2E5E6', '#9AA7AE', '#52A674', '#DC6B6B' local IMG = '/game_correo/images/' local MAX_ADJ = 6 correoController = Controller:new() local ventana, icono, cartel = nil, nil, nil local datos = nil -- última bandeja local abierta = nil -- carta abierta { id, ... , adj } local elegida = nil -- id elegido en la lista local modo = 'nada' -- 'nada' | 'leer' | 'escribir' local borrador = { adj = {} } -- adj[i] = { cid, slot, id, n, item } local destino = nil -- última comprobación { nombre, ok, cuenta } local comprobarEv, mensajeEv, cartelEv, pulsoEv, lejosEv = nil, nil, nil, nil, nil local abiertoEn = nil -- posición del jugador al abrir (se cierra al alejarse) local sinLeer = 0 local function enviar(texto) local protocol = g_game.getProtocolGame() if protocol then protocol:sendExtendedOpcode(OPCODE, texto) end end local function partir(s, sep) local t = {} for x in ((s or '') .. sep):gmatch('(.-)' .. sep:gsub('%p', '%%%0')) do t[#t + 1] = x end return t end local function miles(n) local s = tostring(math.floor(tonumber(n) or 0)) return (s:reverse():gsub('(%d%d%d)', '%1 '):reverse():gsub('^ ', '')) end local function limpio(s) return (tostring(s or ''):gsub('[|^~]', '')) end -- Fundido con salida exponencial (ease-out quart). Solo opacidad. local function fundir(w, desde, hasta, ms, alTerminar) if not w then return end if w.fundido then removeEvent(w.fundido) end local t0 = g_clock.millis() w:setOpacity(desde) local function paso() if w:isDestroyed() then return end local t = math.min(1, (g_clock.millis() - t0) / ms) local e = 1 - (1 - t) ^ 4 w:setOpacity(desde + (hasta - desde) * e) if t < 1 then w.fundido = scheduleEvent(paso, 16) else w.fundido = nil if alTerminar then alTerminar() end end end paso() end local function ocultar(w, ms) if w and w:isVisible() and not w.saliendo then w.saliendo = true fundir(w, w:getOpacity(), 0, ms or 160, function() w.saliendo = nil w:hide() w:setOpacity(1) end) end end local function mensaje(texto, color) if not ventana then return end ventana.mensaje:setText(texto or '') ventana.mensaje:setColor(color or OK) if mensajeEv then removeEvent(mensajeEv) end mensajeEv = scheduleEvent(function() mensajeEv = nil if ventana then ventana.mensaje:setText('') end end, 6000) end -- Lectura de los mensajes --------------------------------------------------------------------------------------------- local function leerBandeja(buffer) local partes = partir(buffer:sub(3), '|') local c = partir(partes[1], '^') local d = { ciudad = c[1] or '', oro = tonumber(c[2]) or 0, franqueo = tonumber(c[3]) or 5, franqueoPaquete = tonumber(c[4]) or 15, maxAdj = tonumber(c[5]) or 6, maxCuerpo = tonumber(c[6]) or 600, demora = tonumber(c[7]) or 30, maxAsunto = tonumber(c[8]) or 40, cartas = {}, enCamino = {}, } for _, l in ipairs(partir(partes[2], '~')) do local m = partir(l, '^') if tonumber(m[1]) then d.cartas[#d.cartas + 1] = { id = tonumber(m[1]), de = m[2], asunto = m[3], fecha = m[4], leida = m[5] == '1', adj = tonumber(m[6]) or 0, oro = tonumber(m[7]) or 0, devuelta = m[8] == '1', dias = tonumber(m[9]) or 0 } end end for _, l in ipairs(partir(partes[3], '~')) do local m = partir(l, '^') if tonumber(m[1]) then d.enCamino[#d.enCamino + 1] = { id = tonumber(m[1]), para = m[2], asunto = m[3], min = tonumber(m[4]) or 0, adj = tonumber(m[5]) or 0, oro = tonumber(m[6]) or 0 } end end return d end local function leerCarta(buffer) local cab, adj = buffer:sub(3):match('^([^|]*)|?(.*)$') local m = partir(cab, '^') if not tonumber(m[1]) or tonumber(m[1]) == 0 then return nil end -- el cuerpo es el último campo (puede traer saltos de línea; nunca | ^ ~) local c = { id = tonumber(m[1]), de = m[2], asunto = m[3], fecha = m[4], oro = tonumber(m[5]) or 0, devuelta = m[6] == '1', dias = tonumber(m[7]) or 0, cuerpo = m[8] or '', adj = {} } for _, l in ipairs(partir(adj, '~')) do local a = partir(l, '^') if tonumber(a[1]) then c.adj[#c.adj + 1] = { i = tonumber(a[1]), id = tonumber(a[2]), n = tonumber(a[3]) or 1, nombre = a[4], tier = tonumber(a[5]) or 0 } end end return c end -- Pintar --------------------------------------------------------------------------------------------------------------- local function iconoDe(c) if c.adj > 0 then return IMG .. 'paquete' end if c.oro > 0 then return IMG .. 'monedas' end return IMG .. (c.leida and 'sobre-abierto' or 'sobre') end local function trae(adj, oro) local t = {} if adj > 0 then t[#t + 1] = adj == 1 and '1 objeto' or (adj .. ' objetos') end if oro > 0 then t[#t + 1] = miles(oro) .. ' oro' end return table.concat(t, ' · ') end local function pestanas() if not ventana then return end local enBandeja = modo ~= 'escribir' ventana.tabBandeja:setOn(enBandeja) ventana.tabBandeja.raya:setVisible(enBandeja) ventana.tabEscribir:setOn(not enBandeja) ventana.tabEscribir.raya:setVisible(not enBandeja) end local function pintarLista() if not ventana or not datos then return end local lista = ventana.lista lista:destroyChildren() local nuevas = 0 for _, c in ipairs(datos.cartas) do if not c.leida then nuevas = nuevas + 1 end local f = g_ui.createWidget('CorreoFila', lista) f.icono:setImageSource(iconoDe(c)) f.quien:setText(c.de) f.quien:setColor(c.leida and TEXTO2 or CLARO) f.asunto:setText(c.asunto) f.asunto:setColor(c.leida and '#9AA7AE99' or TEXTO2) f.fecha:setText(c.fecha:match('^(%S+)') or c.fecha) f.trae:setText(c.devuelta and 'devuelta' or trae(c.adj, c.oro)) f.trae:setColor(c.devuelta and '#D99A45' or ORO) f:setOn(c.id == elegida) f.onClick = function() modules.game_correo.abrirCarta(c.id) end end if #datos.enCamino > 0 then local s = g_ui.createWidget('CorreoSeccion', lista) s:setText('En camino') for _, e in ipairs(datos.enCamino) do local f = g_ui.createWidget('CorreoFila', lista) f.icono:setImageSource(IMG .. 'paquete') f.icono:setOpacity(0.55) f.quien:setText('Para ' .. e.para) f.quien:setColor(TEXTO2) f.asunto:setText(e.asunto) f.asunto:setColor('#9AA7AE99') f.fecha:setText(e.min .. ' min') f.trae:setText(trae(e.adj, e.oro)) f.trae:setColor('#C7A86B99') f:setTooltip(('Llega a %s en unos %d minutos.'):format(e.para, e.min)) f.onClick = nil end end local vacia = #datos.cartas == 0 and #datos.enCamino == 0 ventana.vacia:setVisible(vacia) ventana.vaciaTexto:setVisible(vacia) ventana.vaciaSub:setVisible(vacia) ventana.cuentaBandeja:setText(nuevas > 0 and tostring(nuevas) or '') ventana.ciudad:setText(datos.ciudad) ventana.llevas:setText(miles(datos.oro) .. ' de oro') end local function casilla(padre, i) local w = g_ui.createWidget('CorreoCasilla', padre) w.indice = i return w end local function pintarLectura() if not ventana then return end local p = ventana.leer local c = abierta if not c then return end p.remitente:setText(c.de) p.fecha:setText(c.fecha) p.asunto:setText(c.asunto) p.devuelta:setVisible(c.devuelta) p.cuerpo:setText(c.cuerpo ~= '' and c.cuerpo or ' ') p.cuerpo:setColor(c.cuerpo ~= '' and TEXTO or TEXTO2) if c.cuerpo == '' then p.cuerpo:setText('(Sin texto)') end p.adjuntos:destroyChildren() for i = 1, MAX_ADJ do local w = casilla(p.adjuntos, i) local a = c.adj[i] if a then local it = Item.create(a.id, a.n) if a.tier > 0 then pcall(function() it:setTier(a.tier) end) end -- marco de color de la calidad w:setItem(it) w:setTooltip(a.nombre .. (a.n > 1 and (' x' .. a.n) or '') .. '\nClic para recogerlo.') local idx = a.i w.onClick = function() modules.game_correo.recoger(tostring(idx)) end else w:setOpacity(0.45) end end local hayOro = c.oro > 0 p.oro.cantidad:setText(hayOro and miles(c.oro) or '') p.oro.cantidad:setColor(hayOro and CLARO or TEXTO2) p.oro:setOpacity(hayOro and 1 or 0.45) p.oro:setEnabled(hayOro) p.oro:setTooltip(hayOro and (miles(c.oro) .. ' de oro: clic para pasarlo a tu banco.') or '') p.oro.onClick = hayOro and function() modules.game_correo.recoger('oro') end or nil local algo = #c.adj > 0 or hayOro p.tAdj:setText(algo and 'Adjuntos' or 'Sin adjuntos: ya puedes borrarla') p.caduca:setText(algo and (c.devuelta and 'No caduca' or ('Vuelve al remitente en ' .. c.dias .. (c.dias == 1 and ' día' or ' días'))) or ('Se borra en ' .. c.dias .. (c.dias == 1 and ' día' or ' días'))) p.pista:setText(algo and 'Clic en un objeto para recogerlo solo a él.' or '') -- con adjuntos: recoger y devolver; sin nada que recoger, solo borrar (y sin casillas vacías) p.adjuntos:setVisible(algo) p.oro:setVisible(algo) p.recoger:setVisible(algo) p.devolver:setVisible(algo) p.devolver:setEnabled(not c.devuelta) p.devolver:setTooltip(c.devuelta and 'Ya volvió una vez: no puede devolverse otra.' or 'La carta vuelve entera a quien la mandó.') p.borrar:setVisible(not algo) end local function pintarFranqueo() if not ventana or not datos then return end local e = ventana.escribirPanel local oro = tonumber(e.oroCaja.oro:getText()) or 0 local n = 0 for i = 1, MAX_ADJ do if borrador.adj[i] then n = n + 1 end end local paquete = n > 0 or oro > 0 local coste = paquete and datos.franqueoPaquete or datos.franqueo local cuando if not paquete or datos.demora <= 0 then cuando = 'llega al instante' elseif destino and destino.ok and destino.cuenta then cuando = 'es de tu cuenta: llega al instante' else cuando = ('llega en %d minutos'):format(datos.demora) end e.franqueo:setText(('Franqueo %d de oro · %s'):format(coste, cuando)) e.cuentaCuerpo:setText(#e.cuerpo:getText() .. ' / ' .. datos.maxCuerpo) end local function pintarBorrador() if not ventana then return end local e = ventana.escribirPanel e.adjuntos:destroyChildren() for i = 1, MAX_ADJ do local w = casilla(e.adjuntos, i) local a = borrador.adj[i] if a then local it = Item.create(a.id, a.n) if (a.tier or 0) > 0 then pcall(function() it:setTier(a.tier) end) end w:setItem(it) w:setOn(true) w:setTooltip(a.nombre .. (a.n > 1 and (' x' .. a.n) or '') .. '\nClic para quitarlo.') else w:setTooltip('Suelta aquí un objeto de tu mochila.') end w.onClick = function() borrador.adj[i] = nil pintarBorrador() end w.onDrop = function(self, arrastrado, mousePos) return modules.game_correo.soltar(i, arrastrado) end end pintarFranqueo() end local function verModo(m) modo = m if not ventana then return end ventana.leer:setVisible(m == 'leer') ventana.escribirPanel:setVisible(m == 'escribir') ventana.nada:setVisible(m == 'nada') pestanas() end -- La columna derecha del HUD (minimapa y Bolsas) mide unos 320 px: la ventana no la tapa, para poder arrastrar -- desde la mochila. En pantallas anchas queda centrada; en 1366 se corre a la izquierda. local COLUMNA_HUD = 330 local function colocar() local guardadas = g_settings.getNode('otmmo-ventanas') if type(guardadas) == 'table' and guardadas.correo then return end -- el jugador la movió: OtmmoVentanas manda local ancho = rootWidget:getWidth() local w = ventana:getWidth() local x = math.floor((ancho - w) / 2) if x + w > ancho - COLUMNA_HUD then x = math.max(8, ancho - COLUMNA_HUD - w) end ventana:breakAnchors() ventana:addAnchor(AnchorVerticalCenter, 'parent', AnchorVerticalCenter) ventana:addAnchor(AnchorLeft, 'parent', AnchorLeft) ventana:setMarginLeft(x) end local function abrir() if not ventana:isVisible() then colocar() ventana:show() fundir(ventana, 0, 1, 220) end ventana:raise() ventana:focus() local yo = g_game.getLocalPlayer() abiertoEn = yo and yo:getPosition() end -- Acciones (onClick de la ventana) -------------------------------------------------------------------------------------- function cerrar() ocultar(ventana) abiertoEn = nil end function verBandeja() if modo == 'escribir' then verModo(abierta and 'leer' or 'nada') end end function escribir(para) verModo('escribir') if para then ventana.escribirPanel.para:setText(para) end pintarBorrador() ventana.escribirPanel.para:focus() end function abrirCarta(id) elegida = id enviar('L|' .. id) if datos then pintarLista() end end function recoger(que) if abierta then enviar('R|' .. abierta.id .. '|' .. que) end end function devolver() if abierta then enviar('V|' .. abierta.id) end end function borrar() if abierta then enviar('B|' .. abierta.id) end end -- El cliente no sabe el nombre en español (lo pone el servidor al enviar): el del mercado de Tibia o nada. function nombreCliente(item) local ok, md = pcall(function() return item:getThingType():getMarketData() end) local n = ok and md and md.name or '' return n ~= '' and n or 'Objeto' end -- Suelta un objeto arrastrado desde un contenedor en la casilla i del borrador. function soltar(i, arrastrado) local item = arrastrado and arrastrado.currentDragThing if not item or not item.isItem or not item:isItem() then return false end local pos = item:getPosition() if not pos or pos.x ~= 65535 or pos.y < 64 then ventana.escribirPanel.aviso:setText('Solo se mandan objetos de la mochila (no lo que llevas puesto ni el suelo).') return true end for k, a in pairs(borrador.adj) do if k ~= i and a.item == item then borrador.adj[k] = nil end end borrador.adj[i] = { cid = pos.y - 64, slot = pos.z, id = item:getId(), n = item:getCount(), item = item, nombre = nombreCliente(item), tier = (function() local ok, t = pcall(function() return item:getTier() end) return ok and tonumber(t) or 0 end)() } ventana.escribirPanel.aviso:setText('') pintarBorrador() return true end function mandar() if not ventana or not datos then return end local e = ventana.escribirPanel local para = limpio(e.para:getText()):gsub('^%s+', ''):gsub('%s+$', '') if para == '' then e.aviso:setText('Escribe a quién va la carta.') e.para:focus() return end local oro = tonumber(e.oroCaja.oro:getText()) or -1 if oro < 0 or oro ~= math.floor(oro) then e.aviso:setText('El oro tiene que ser un número entero.') return end local refs = {} for i = 1, MAX_ADJ do local a = borrador.adj[i] if a then -- el objeto pudo moverse dentro de la mochila: se manda donde está ahora local pos = a.item and a.item.getPosition and a.item:getPosition() if pos and pos.x == 65535 and pos.y >= 64 then a.cid, a.slot = pos.y - 64, pos.z end refs[#refs + 1] = ('%d:%d:%d:%d'):format(a.cid, a.slot, a.id, a.n) end end e.aviso:setText('') e.enviar:setEnabled(false) scheduleEvent(function() if ventana then ventana.escribirPanel.enviar:setEnabled(true) end end, 1500) enviar('E|' .. table.concat({ para, limpio(e.asunto:getText()), limpio(e.cuerpo:getText()), oro }, '^') .. '|' .. table.concat(refs, '~')) end local function comprobarDestino() if comprobarEv then removeEvent(comprobarEv) end local e = ventana.escribirPanel destino = nil e.paraEstado:setText('') pintarFranqueo() local t = limpio(e.para:getText()):gsub('^%s+', ''):gsub('%s+$', '') if #t < 2 then return end comprobarEv = scheduleEvent(function() comprobarEv = nil enviar('C|' .. t) end, 500) end local function vaciarBorrador() local e = ventana.escribirPanel e.para:setText('') e.asunto:setText('') e.cuerpo:setText('') e.oroCaja.oro:setText('0') e.aviso:setText('') e.paraEstado:setText('') borrador.adj = {} destino = nil pintarBorrador() end -- Icono y aviso de carta nueva ------------------------------------------------------------------------------------------ local function colocarIcono() if not icono then return end -- a la izquierda del minimapa, a la altura de su borde de abajo (se mueve con él) local marco = rootWidget:recursiveGetChildById('marcoMinimapa') local padre = marco and marco:getParent() if padre and icono:getParent() ~= padre then icono:setParent(padre) icono:breakAnchors() icono:addAnchor(AnchorBottom, 'marcoMinimapa', AnchorBottom) icono:addAnchor(AnchorRight, 'marcoMinimapa', AnchorLeft) icono:setMarginBottom(4) icono:setMarginRight(8) end end local function pintarIcono(n, pulso) sinLeer = n if not icono then return end colocarIcono() icono:setVisible(n > 0) icono.n:setText(tostring(n)) icono:setTooltip(n == 1 and 'Una carta sin leer. Ábrela en un buzón de la Liga del Puerto.' or (n .. ' cartas sin leer. Ábrelas en un buzón de la Liga del Puerto.')) if pulso and n > 0 then if pulsoEv then removeEvent(pulsoEv) end local t0 = g_clock.millis() local function paso() pulsoEv = nil if not icono or icono:isDestroyed() then return end local t = (g_clock.millis() - t0) / 1000 if t >= 1.6 then icono:setOpacity(1) return end icono:setOpacity(0.55 + 0.45 * math.cos(t * math.pi * 2.5) ^ 2) pulsoEv = scheduleEvent(paso, 33) end paso() end end local function mostrarCartel(de, asunto) if not cartel then return end cartel.etiqueta:setText('Carta nueva') if de and de ~= '' then cartel.sub:setText('De ' .. de .. (asunto and asunto ~= '' and (' · ' .. asunto) or '')) else cartel.sub:setText('Te espera en cualquier buzón de la Liga del Puerto') end if cartelEv then removeEvent(cartelEv) end cartelEv = scheduleEvent(function() cartel.saliendo = nil cartel:show() cartel:raise() fundir(cartel, 0, 1, 450, function() cartelEv = scheduleEvent(function() cartelEv = nil ocultar(cartel, 700) end, 3200) end) end, 400) end -- Buzones del mapa («P»): encima del buzón de siempre (3501) se pega el de la Liga en pixel art como efecto adjunto con -- textura propia (como los portales de game_viaje): no hace falta tocar las apariencias. Usar sigue siendo el 3501. local BUZON_EFECTO = 1550 local buzones = {} local function pegarBuzones() local yo = g_game.isOnline() and g_game.getLocalPlayer() if not yo or #buzones == 0 or not g_attachedEffects then return end if not g_attachedEffects.getById(BUZON_EFECTO) then local e = g_attachedEffects.registerByImage(BUZON_EFECTO, 'Buzon de la Liga', IMG .. 'buzon-mapa') if not e then return end e:setSize({ width = 64, height = 64 }) e:setOffset(16, 32) e:setPermanent(true) end local p = yo:getPosition() if not p then return end for _, b in ipairs(buzones) do if b.z == p.z and math.abs(b.x - p.x) <= 10 and math.abs(b.y - p.y) <= 8 then local tile = g_map.getTile(b) if tile and not tile:getAttachedEffectById(BUZON_EFECTO) then tile:attachEffect(g_attachedEffects.getById(BUZON_EFECTO)) end end end end -- Opcode ---------------------------------------------------------------------------------------------------------------- local function onOpcode(protocol, opcode, buffer) local c = buffer:sub(1, 1) if c == 'B' then datos = leerBandeja(buffer) local existe = false for _, x in ipairs(datos.cartas) do if x.id == elegida then existe = true end end if not existe then elegida, abierta = nil, nil if modo == 'leer' then verModo('nada') end end pintarLista() if modo == 'escribir' then pintarFranqueo() end if not ventana:isVisible() and buffer:sub(-2) == '|1' then -- solo al usar el buzón verModo(abierta and 'leer' or 'nada') abrir() end elseif c == 'L' then abierta = leerCarta(buffer) if abierta then elegida = abierta.id for _, x in ipairs(datos and datos.cartas or {}) do if x.id == abierta.id then x.leida = true end end pintarLectura() verModo('leer') else elegida = nil verModo('nada') end pintarLista() elseif c == 'D' then local m = partir(buffer:sub(3), '^') local e = ventana.escribirPanel local escrito = limpio(e.para:getText()):gsub('^%s+', ''):gsub('%s+$', ''):lower() if (m[1] or ''):lower() ~= escrito then return end destino = { nombre = m[1], ok = m[2] == '1', cuenta = m[3] == '1' } e.paraEstado:setText(destino.ok and (destino.cuenta and m[4] or ('Carta para ' .. destino.nombre)) or (m[4] or '')) e.paraEstado:setColor(destino.ok and OK or ERROR) pintarFranqueo() elseif c == 'M' then local t = buffer:sub(3) if not ventana:isVisible() or ventana.saliendo then -- p. ej. «acércate a un buzón» justo después de «X»: la ventana ya no se ve modules.game_textmessage.displayFailureMessage(t) elseif modo == 'escribir' then ventana.escribirPanel.aviso:setText(t) else mensaje(t, ERROR) end elseif c == 'S' then vaciarBorrador() verModo(abierta and 'leer' or 'nada') mensaje(buffer:sub(3), OK) elseif c == 'N' then local m = partir(buffer:sub(3), '^') pintarIcono(tonumber(m[1]) or sinLeer, true) -- con el buzón abierto la carta ya aparece en la lista: sin cartel encima if not (ventana and ventana:isVisible()) then mostrarCartel(m[2], m[3]) end elseif c == 'I' then pintarIcono(tonumber(buffer:sub(3)) or 0, false) elseif c == 'X' then cerrar() elseif c == 'P' then buzones = {} for _, l in ipairs(partir(buffer:sub(3), '~')) do local m = partir(l, '^') if tonumber(m[3]) then buzones[#buzones + 1] = { x = tonumber(m[1]), y = tonumber(m[2]), z = tonumber(m[3]) } end end pcall(pegarBuzones) end end -- Para probar sin servidor: modules.game_correo.probar('B|...') function probar(texto) onOpcode(nil, OPCODE, texto) end -- Se cierra al alejarse del buzón (el servidor también lo comprueba en cada petición). local function vigilar() if not ventana or not ventana:isVisible() or not abiertoEn then return end local yo = g_game.getLocalPlayer() local p = yo and yo:getPosition() if p and (p.z ~= abiertoEn.z or math.max(math.abs(p.x - abiertoEn.x), math.abs(p.y - abiertoEn.y)) > 3) then cerrar() end end function correoController:onInit() g_ui.importStyle('/game_viaje/valdren') g_ui.importStyle('correo') ventana = g_ui.displayUI('correo') if OtmmoVentanas then OtmmoVentanas.recordar(ventana, 'correo') end ventana:hide() local e = ventana.escribirPanel e.para.onTextChange = function() comprobarDestino() end e.cuerpo.onTextChange = function() pintarFranqueo() end e.oroCaja.oro.onTextChange = function(w, t) local s = t:gsub('%D', '') if s ~= t then w:setText(s) return end pintarFranqueo() end icono = g_ui.createWidget('CorreoIcono', rootWidget) icono:addAnchor(AnchorTop, 'parent', AnchorTop) icono:addAnchor(AnchorRight, 'parent', AnchorRight) icono:setMarginTop(210) icono:setMarginRight(12) cartel = g_ui.createWidget('CartelCarta', rootWidget) cartel:addAnchor(AnchorTop, 'parent', AnchorTop) cartel:addAnchor(AnchorHorizontalCenter, 'parent', AnchorHorizontalCenter) cartel:setMarginTop(150) verModo('nada') ProtocolGame.registerExtendedOpcode(OPCODE, onOpcode) lejosEv = cycleEvent(function() vigilar() pcall(pegarBuzones) end, 600) end function correoController:onGameStart() scheduleEvent(colocarIcono, 1500) end function correoController:onGameEnd() datos, abierta, elegida, destino, abiertoEn = nil, nil, nil, nil, nil borrador.adj = {} sinLeer = 0 for _, w in ipairs({ ventana, cartel, icono }) do if w then w:hide() w:setOpacity(1) end end end function correoController:onTerminate() self:onGameEnd() for _, ev in ipairs({ comprobarEv, mensajeEv, cartelEv, pulsoEv, lejosEv }) do if ev then removeEvent(ev) end end ProtocolGame.unregisterExtendedOpcode(OPCODE) for _, w in ipairs({ ventana, cartel, icono }) do if w then w:destroy() end end ventana, cartel, icono = nil, nil, nil end