local CODE = 108 -- Configura��o local SOUNDS_CONFIG = { areaSoundFolder = 'sounds/', -- Pasta onde ficam as m�sicas de �rea effectSoundFolder= 'sounds/', -- Pasta onde ficam efeitos sonoros checkInterval = 500, -- Intervalo (ms) para verificar se o player est� numa �rea noSound = 'No sound file for this area.' -- Texto exibido na interface quando n�o h� som } -- �reas que t�m m�sica de fundo local AREAS = { { fromPos = {x = 3967, y = 936, z = 8}, toPos = {x = 4038, y = 1000, z = 8}, sound = "cave.ogg" }, { fromPos = {x = 3921, y = 904, z = 7}, toPos = {x = 4086, y = 1006, z = 7}, sound = "gaivotas.ogg" } } -- Vari�veis globais local areaChannel -- Canal para m�sica de �rea (background) local effectChannel -- Canal para efeitos sonoros pontuais local playingAreaSound = nil -- Qual m�sica de �rea est� tocando agora local toggleSoundEvent = nil -- Evento agendado para checar posi��o periodicamente --[[ FUN��O PRINCIPAL DE INICIALIZA��O Chamada quando o script do OTClient � carregado. --]] function init() -- Conecta eventos do jogo connect(g_game, { onGameStart = onGameStart, onGameEnd = onGameEnd }) -- Registra opcode para sons de efeito vindos do servidor ProtocolGame.registerExtendedOpcode(CODE, onExtendedOpcode) -- Cria canais de som areaChannel = g_sounds.getChannel(SoundChannels.Music) -- Canal de m�sica de fundo effectChannel = g_sounds.getChannel(SoundChannels.Effects) -- Canal de efeitos -- Bot�o na interface soundButton = modules.client_topmenu.addRightGameToggleButton( 'soundButton', 'Som', '/images/audio', toggleSoundWindow ) soundButton:setOn(true) -- Carrega a janela "rcsound.otui" soundWindow = g_ui.loadUI('rcsound', modules.game_interface.getRightPanel()) soundWindow:disableResize() soundWindow:setup() if g_game.isOnline() then onGameStart() end end --[[ FUN��O DE FINALIZA��O Chamada quando o script � descarregado do OTClient. --]] function terminate() disconnect(g_game, { onGameStart = onGameStart, onGameEnd = onGameEnd }) ProtocolGame.unregisterExtendedOpcode(CODE) onGameEnd() soundWindow:destroy() soundButton:destroy() end --[[ Chamado quando o jogo fica online (player loga) --]] function onGameStart() stopAreaSound() toggleSoundEvent = addEvent(checkAreaSound, SOUNDS_CONFIG.checkInterval) end --[[ Chamado quando o jogo fica offline (player desloga) --]] function onGameEnd() stopAreaSound() if toggleSoundEvent then removeEvent(toggleSoundEvent) toggleSoundEvent = nil end end --[[ Abrir/fechar janela de som --]] function toggleSoundWindow() if soundButton:isOn() then soundWindow:close() soundButton:setOn(false) else soundWindow:open() soundButton:setOn(true) end end function onMiniWindowClose() if soundButton then soundButton:setOn(false) end end --[[ Checa se a posi��o "pos" est� dentro do ret�ngulo 3D [fromPos -> toPos] --]] function isInPos(pos, fromPos, toPos) return (pos.x >= fromPos.x and pos.y >= fromPos.y and pos.z >= fromPos.z) and (pos.x <= toPos.x and pos.y <= toPos.y and pos.z <= toPos.z) end --[[ Fun��o principal que verifica a �rea do player a cada "checkInterval" e decide se deve tocar/parar a m�sica de �rea. --]] function checkAreaSound() local player = g_game.getLocalPlayer() if not player then return end local pos = player:getPosition() local areaSoundData = nil for _, area in ipairs(AREAS) do if isInPos(pos, area.fromPos, area.toPos) then areaSoundData = area break end end if areaSoundData then -- Se � outra m�sica, troca if playingAreaSound ~= areaSoundData.sound then stopAreaSound() local fullPath = SOUNDS_CONFIG.areaSoundFolder .. areaSoundData.sound if g_resources.fileExists(fullPath) then g_logger.info("Tocando m�sica de �rea: " .. fullPath) playAreaSound(fullPath) playingAreaSound = areaSoundData.sound else g_logger.warning("Arquivo de m�sica n�o encontrado: " .. fullPath) end end else -- Se n�o est� em �rea, parar se estava tocando algo if playingAreaSound then g_logger.info("Parando m�sica de �rea.") stopAreaSound() end end toggleSoundEvent = scheduleEvent(checkAreaSound, SOUNDS_CONFIG.checkInterval) end --[[ Toca a m�sica de �rea no canal "areaChannel" Ajuste o volume inicial se quiser mais alto/baixo (0.1 = 10%, 0.4 = 40% etc.). --]] function playAreaSound(soundPath) if not areaChannel then print("Canal de �rea n�o dispon�vel!") return end areaChannel:play(soundPath, 0, 0.1) areaChannel:setGain(0.1) setLabel(soundPath) end --[[ Para a m�sica de �rea --]] function stopAreaSound() if areaChannel then areaChannel:stop() end playingAreaSound = nil setLabel(SOUNDS_CONFIG.noSound) end --[[ Recebe opcode do servidor com algum som extra (efeito) para tocar --]] function onExtendedOpcode(protocol, code, buffer) if code ~= CODE then return end local ok, data = pcall(function() return json.decode(buffer) end) if not ok then print("Erro ao decodificar JSON: " .. data) return end local soundFile = data.data local fullPath = SOUNDS_CONFIG.effectSoundFolder .. soundFile if g_resources.fileExists(fullPath) then playEffectSound(fullPath) else print("Som n�o encontrado: " .. fullPath) end end --[[ Toca um efeito sonoro e reduz momentaneamente o volume da m�sica de �rea para destacar o efeito. --]] function playEffectSound(soundPath) if not effectChannel then print("Canal de efeito n�o dispon�vel!") return end -- Se a m�sica de �rea est� tocando, diminui volume if playingAreaSound and areaChannel then areaChannel:setGain(0.1) -- Ajuste esse valor se quiser mais baixo durante o efeito end effectChannel:play(soundPath, 0, 1.0) effectChannel:setGain(1.0) -- Precisamos descobrir a dura��o do efeito para restaurar o volume depois local duration = getSoundDuration(soundPath) or 3000 scheduleEvent(function() -- Restaura volume da m�sica de �rea if playingAreaSound and areaChannel then areaChannel:setGain(0.1) -- volta ao volume anterior end end, duration) end --[[ Se n�o tem leitura de metadados, definimos 3 segundos como padr�o --]] function getSoundDuration(soundPath) return 3000 end --[[ Atualiza o texto na janela "rcsound" com o nome do som atual --]] function setLabel(str) soundWindow:recursiveGetChildById('currentSound'):getChildById('value'):setText(str) end