-- 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()