-- reactor_alert_v2.lua -- Alarma para reactor: detecta redstone por abajo y muestra alerta parpadeante en monitor top -- y emite sonido por el speaker en back (o usa fallback de redstone si no hay método disponible). local inputSide = "bottom" -- señal de entrada local monitorSide = "top" -- monitor para la alerta local speakerSide = "back" -- speaker (periférico) o lado donde encender redstone como fallback -- parámetros local blinkInterval = 0.5 -- intervalo de parpadeo (s) local soundInterval = 0.12 -- intervalo entre reproducciones de sonido (s) local bgOn1 = colors.red local bgOn2 = colors.orange or colors.red -- si orange no existe, red se usa (compatibilidad) local bgOff = colors.black local textColor = colors.white -- Helpers de periféricos local function hasPeripheral(side) return peripheral.isPresent(side) end local function wrapPeripheral(side) if peripheral.isPresent(side) then return peripheral.wrap(side) end return nil end -- Dibuja el mensaje centrado en el monitor con fondo y texto dados local function drawMessageOnMonitor(mon, w, h, text, bg) mon.setBackgroundColor(bg) mon.setTextColor(textColor) mon.clear() local x = math.max(1, math.floor((w - #text) / 2) + 1) local y = math.max(1, math.floor(h / 2)) mon.setCursorPos(x, y) mon.write(text) end -- Limpia monitor (restaurar fondo negro y borrar) local function clearMonitor(mon) mon.setBackgroundColor(bgOff) mon.clear() mon.setCursorPos(1,1) end -- Crea un "player" de sonido robusto: -- intenta detectar métodos en el periférico, si no, devuelve nil y usaremos fallback redstone. local function makeSoundPlayer() local p = wrapPeripheral(speakerSide) if p then -- averiguamos métodos disponibles local methods = {} for k,_ in pairs(p) do table.insert(methods, k) end -- métodos comunes que intentaremos usar (orden de preferencia) local tryList = { -- nombre => function(args...) que realiza una llamada segura {name="playSound", call=function(...) return p.playSound(...) end}, {name="play", call=function(...) return p.play(...) end}, {name="playNote", call=function(...) return p.playNote(...) end}, {name="playTone", call=function(...) return p.playTone(...) end}, {name="beep", call=function(...) return p.beep(...) end}, {name="note", call=function(...) return p.note(...) end}, } for _, t in ipairs(tryList) do if type(p[t.name]) == "function" then -- probamos una llamada segura pcall para confirmar que funciona local ok = pcall(p[t.name], p, "harp", 1) -- intento genérico; si falla no pasa nada if ok then -- devolvemos una función que reproducirá sonido periódicamente mientras shouldRun() sea true local name = t.name return function(shouldRun) while shouldRun() do pcall(p[name], p, "harp", 1) -- usamos parámetros genéricos; algunos métodos los ignorarán sleep(soundInterval) end end end end end -- si no encontramos un método estándar pero el peripheral tiene 'playNote', usarlo if type(p.playNote) == "function" then return function(shouldRun) while shouldRun() do pcall(p.playNote, p, "harp", 1) sleep(soundInterval) end end end end -- No hay periférico con método válido -> fallback con redstone toggle en speakerSide return nil end -- Funcion que activa/desactiva la señal redstone de fallback de sonido local function setSpeakerRedstone(state) pcall(redstone.setOutput, speakerSide, state) end -- Rutina de parpadeo del monitor: retorna la función que detiene el parpadeo (stopFunc) local function startMonitorBlink(text) if not hasPeripheral(monitorSide) then -- fallback: escribir en terminal local local function stop() term.setBackgroundColor(bgOff) term.clear() term.setCursorPos(1,1) end -- parpadeo en terminal: alternar fondo local running = true local co = coroutine.create(function() while running do term.setBackgroundColor(bgOn1) term.clear() term.setCursorPos(1,1) print(text) sleep(blinkInterval) term.setBackgroundColor(bgOff) term.clear() sleep(blinkInterval) end end) coroutine.resume(co) return function() running = false; coroutine.resume(co); stop() end end local mon = peripheral.wrap(monitorSide) local w,h = mon.getSize() local running = true local co = coroutine.create(function() local toggle = true while running do if toggle then drawMessageOnMonitor(mon, w, h, text, bgOn1) else drawMessageOnMonitor(mon, w, h, text, bgOn2) end toggle = not toggle -- dormir en trozos para permitir salida rápida si se para local slept = 0 while slept < blinkInterval and running do sleep(0.05) slept = slept + 0.05 end end clearMonitor(mon) end) coroutine.resume(co) return function() running = false; coroutine.resume(co) end end -- MAIN: monitorizamos la entrada y activamos/desactivamos alarma local function main() -- Texto de alerta sin signos de apertura, solamente cierre de exclamación si quieres: local alertText = "ALERTA REACTOR !!!" -- solo signos de cierre local soundPlayer = makeSoundPlayer() local alarmOn = false local stopMonitorFunc = nil local soundThread = nil local soundRunning = false print("Alarma lista. Esperando señal en " .. inputSide .. " ...") while true do -- chequeo simple del estado actual local sig = redstone.getInput(inputSide) if sig and not alarmOn then -- activar alarma alarmOn = true print("Señal detectada -> ACTIVANDO alarma.") -- lanzar parpadeo en monitor stopMonitorFunc = startMonitorBlink(alertText) -- iniciar sonido if soundPlayer then soundRunning = true soundThread = parallel.waitForAny(function() soundPlayer(function() return soundRunning end) end, function() while soundRunning do sleep(1) end end) -- note: the above construction keeps it non-blocking by running in parallel later controlled via soundRunning -- but we'll instead run sound in its own coroutine for better control: soundRunning = true local co = coroutine.create(function() soundPlayer(function() return soundRunning end) end) coroutine.resume(co) soundThread = co else -- fallback: toggle redstone rapidly to simulate continuous sound soundRunning = true local co2 = coroutine.create(function() while soundRunning do setSpeakerRedstone(true) sleep(soundInterval) setSpeakerRedstone(false) sleep(0.02) end setSpeakerRedstone(false) end) coroutine.resume(co2) soundThread = co2 end elseif not sig and alarmOn then -- desactivar alarma alarmOn = false print("Señal perdida -> DESACTIVANDO alarma.") -- parar sonido soundRunning = false setSpeakerRedstone(false) -- parar monitor if stopMonitorFunc then stopMonitorFunc() stopMonitorFunc = nil end end -- dormir un poco y esperar siguiente cambio -- usamos pullEvent con timeout para reaccionar rápido pero seguir el lazo local ev = { os.pullEventTimeout("redstone", 1) } -- espera redstone o timeout 1s -- si evento es nil (timeout) seguimos el lazo y reevaluamos el estado end end -- Ejecutar main()